ores.scheduler.job_definition

Table of Contents

A job the scheduler fires on a cron expression. The row carries the schedule (schedule_expression), what to run when it fires, and whether it is active. action_type selects the behaviour: execute_sql runs the SQL in command, and nats_publish publishes the subject and body carried in action_payload.

The table is bi-temporal and audited (see projects/ores.sql/create/scheduler/scheduler_job_definitions_create.sql): it carries version, the four audit columns and the valid_from=/=valid_to pair with the GIST exclusion and the delete rule, so the model takes the ordinary audited shape and needs no shape flag.

tenant_id is nullable. A job may belong to no tenant, because the scheduler fires system jobs from a NULL-tenant row: the MQ statistics scrape and the compute stale-result reaper are both such rows. The model binds uuid-identified-lookup for its UUID surrogate key, its tenant scope and its standard presentation tier, and states nullable_tenant_id itself, which that profile leaves to the model.

job_name is the natural key and is unique within its tenant; id is the surrogate. Uniqueness is what the component's upsert path relies on: a job that arrives under an existing name updates that row in place instead of adding a second one.

The component's operational views — the global job-instance list and the live scheduler status — are operations rather than entity verbs, and are modelled in ores.scheduler.scheduling_operations.

1. Flags

2. Natural keys

3. Columns

3.1. job_name

Unique name for the job within its tenant. The scheduler seed writes ores.mq.metrics_scrape and the compute seed writes compute.v1.reap.stale_results; both are system jobs, so both carry a NULL tenant and neither collides with a tenant's own job of the same name.

3.2. id

UUID primary key for the job definition.

3.3. party_id

Optional party scope for this job. NULL for a tenant-scoped job and for a system job.

3.4. description

Human-readable description of the job.

3.5. command

SQL command to execute when action_type is execute_sql.

3.6. schedule_expression

Cron expression defining the schedule. It is the component's own validated cron_expression type, so an invalid expression cannot enter the domain.

3.7. action_type

Execution mode: execute_sql or nats_publish.

3.8. action_payload

Payload for the nats_publish action type. The scheduler seed writes an empty object; the compute seed writes {"subject":"compute.v1.work.reap"}.

3.9. is_active

Whether the job fires. A paused job keeps its row and its history.

4. SQL

4.1. Flags

5. Physical space

The shell commands for this entity are switched off. The ores.shell composite does not carry a scheduler part yet, and the shell command registry is hand-written, so enabling the facet here would generate command units and a recipe for a module no build reaches. Adding the part to the shell component, registering its commands, and then enabling this facet is the next unit of the scheduler clean-up; the profile enables the facet, so this table states the reason it is overridden.

Address Enabled
ores.cpp.shell-command false

6. C++

6.1. Flags

6.2. Repository

6.3. Domain includes

#include <chrono>
#include <optional>
#include <string>
#include <boost/uuid/uuid.hpp>

6.4. Conventions

6.5. Table display

column header
id ID
job_name Job Name
description Description
schedule_expression Schedule
is_active Active
modified_by Modified By
version Version

6.6. Custom repository methods

The generated CRUD set reads by primary key and by the natural key. The scheduler loop needs a third read: every active job, across every tenant, because it fires system jobs as well as tenant ones. That read is the one method beyond the generated set, and it cannot be expressed by the generated by-column finder because it deliberately drops the tenant filter.

6.6.1. Repository declarations

/**
 * @brief Reads every active job definition, across all tenants.
 *
 * The scheduler loop fires system jobs as well as tenant ones, so it
 * cannot read through the tenant-scoped CRUD set.
 */
std::vector<domain::job_definition> read_all_active(context ctx);

6.6.2. Repository implementations

std::vector<domain::job_definition>
job_definition_repository::read_all_active(context ctx) {
    BOOST_LOG_SEV(lg(), debug) << "Reading all active job definitions.";
    static const auto max(make_timestamp(MAX_TIMESTAMP, lg()));
    const auto query = sqlgen::read<std::vector<job_definition_entity>> |
                       where("is_active"_c == true && "valid_to"_c == max.value());

    return execute_read_query<job_definition_entity, domain::job_definition>(
        ctx,
        query,
        [](const auto& entities) { return job_definition_mapper::map(entities); },
        lg(),
        "Reading all active job definitions.");
}

7. See also

Emacs 29.3 (Org mode 9.6.15)