# Scheduled queries

> Running queries on a schedule

Source: https://orbitalhq.com/docs/querying/scheduled-queries

Orbital supports running saved queries automatically on a schedule. This is useful for scenarios such as:

* Polling an external API every few minutes
* Refreshing a dataset at a regular interval
* Running nightly sync or cleanup jobs

Scheduled queries complement existing [HTTP Endpoints](/docs/querying/queries-as-endpoints) and [Background Streams](/docs/querying/streaming-data#running-long-lived-streaming-queries), giving you multiple ways to publish and reuse queries.

## Defining a Scheduled Query

You declare a scheduled query using the `@Scheduled` annotation:

```taxi
>   import com.orbitalhq.scheduler.Scheduled

>   @Scheduled(cron = "0 0/5 * * * ?")
    query RefreshProducts {
       find { Product[] }
    }
```

Be sure to include the import statement: `import com.orbitalhq.scheduler.Scheduled`

This runs the query `RefreshProducts` every 5 minutes.
Under the hood, Orbital uses [Quartz Scheduler](https://www.quartz-scheduler.org/) with a JDBC-backed job store, so schedules are **persistent and cluster-safe**.

## Annotation Reference

```taxi
namespace com.orbitalhq.scheduler

annotation Scheduled {
  /**
   * Cron expression (Quartz format)
   */
  cron: String?

  /**
   * Run with fixed rate (ISO-8601 duration or shorthand like "10m")
   */
  fixedRate: String?

  /**
   * Max number of executions (-1 = forever)
   */
  repeatCount: Int = -1

  /**
   * What to do if a fire is missed
   */
  misfirePolicy: MisfirePolicy = MisfirePolicy.Default

  /**
   * Trigger priority (higher runs first)
   */
  priority: Int = 5
}

enum MisfirePolicy {
  Default,
  FireImmediately,
  Skip
}

```

| Property          | Description                                                                                                                                                                                  | Example / Values                                                                                                                                   | Default                                  |
|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------|
| **cron**          | A [Quartz cron expression](https://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html) with 6 fields: seconds, minutes, hours, day-of-month, month, day-of-week. | `0 0 2 * * ?` → 2:00 AM daily                                                                                                                      | *None* (required if `fixedRate` not set) |
| **fixedRate**     | Run at a fixed interval. Supports ISO-8601 durations or shorthand.                                                                                                                           | `PT10M` or `10m`                                                                                                                                   | *None* (required if `cron` not set)      |
| **repeatCount**   | Number of times to run.                                                                                                                                                                      | `-1` = forever                                                                                                                                     | `-1`                                     |
| **misfirePolicy** | How to handle missed runs.                                                                                                                                                                   | `Default` (Quartz’s smart policy, usually fire once now) `FireImmediately` (catch up immediately) `Skip` (ignore missed runs, wait for next) | `Default`                                |
| **priority**      | Trigger priority if multiple jobs fire simultaneously.                                                                                                                                       | `5` (default), higher numbers run first                                                                                                            | `5`                                      |

**Note: Provide either cron or fixedRate**
Exactly one of `cron` or <cron>fixedRate</cron> must be provided.

## Supported Query Types

Scheduled queries must be **finite queries** (`find` or `call`).
Streaming queries (`stream { }`) cannot be scheduled.

## Admin & Monitoring

Scheduled queries are shown in the Orbital UI under the **Saved Queries** tab.

Query results from each scheduled run are stored in Orbital's query history, if enabled.

## Cluster Behavior

When running in a cluster, job scheduling is co-ordinated across the cluster, using Quartz and the Postgres job store to coordinate execution. 

At any given time, exactly one node will execute a scheduled query, ensuring there are no duplicates. 
If a node crashes or becomes unavailable, another node in the cluster automatically takes over future runs.

## Examples

**Hourly polling job**:

```taxi
>  import com.orbitalhq.scheduler.Scheduled   
   
>  @Scheduled(fixedRate = "1h")
   query PollApi {
      call { ExternalApi[] }
   }
```

**Nightly batch job**:

```taxi
@Scheduled(cron = "0 0 2 * * ?", misfirePolicy = FireImmediately)
query NightlySync {
   find { Orders }
}
```

## Limitations

* No support yet for multiple schedules per query (one `@Scheduled` per query).
* Missed runs are not replayed unless `misfirePolicy` is set.
