> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flashduty.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Database Pull Alert Integration (DB Pull)

> Flashduty periodically queries a MySQL, PostgreSQL, or ClickHouse database and converts matching rows into standard alert events. Use this when alert data is already written to a relational database and the system cannot push alerts outward.

By configuring a database connection and a SELECT query, Flashduty connects to your database on the schedule you define, incrementally fetches new rows via cursor-based pagination, and converts each row into a [Standard Alert Event](/en/on-call/integration/alert-integration/alert-sources/standard%20alert) using field mapping rules. Your system does not need to support push delivery.

:::tips
DB Pull is the right choice when **alert data is already stored in a relational database** and you **cannot or do not want to modify the existing system to support webhook push**. If the database is on a network that Flashduty cannot reach, consider exporting alerts to a publicly accessible HTTP endpoint and using the HTTP Pull integration instead.
:::

<div className="hide">
  ## Setup Steps

  ***

  ### In Flashduty

  <details>
    <summary>Using Dedicated Integration</summary>

    When you don't need to route alert events to different channels, this method is preferred — it's simpler.

    1. Enter the Flashduty console, select **Channel**, and enter the details page of a channel.
    2. Select the **Integrations** tab, click **Add Integration** to enter the add integration page.
    3. Select the **DB Pull** integration.
    4. Fill in the form as described in the "Configuration" section below and click **Save**.
  </details>

  <details>
    <summary>Using Shared Integration</summary>

    When you need to route alert events to different channels based on the alert payload, choose this method.

    1. Enter the Flashduty console, select **Integration Center => Alert Events** to enter the integration selection page.
    2. Select the **DB Pull** integration and fill in the **Integration Name**.
    3. Configure the **Default Route** and select the corresponding channel (after the integration is created, go to **Route** to add finer-grained routing rules).
    4. Fill in the form as described in the "Configuration" section below and click **Save**.
  </details>
</div>

## Configuration

***

### Database Driver

|  Field | Required | Description                                                                                                                                      |
| :----: | :------: | :----------------------------------------------------------------------------------------------------------------------------------------------- |
| Driver |    Yes   | The database type. Supported values: `mysql`, `postgres` (or `postgresql`), `clickhouse`. Case-insensitive — the system normalizes to lowercase. |

### Connection

|     Field    | Required | Description                                                                                                                                                              |
| :----------: | :------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|     Host     |    Yes   | Hostname or IP address of the database server. **Loopback addresses (127.x.x.x / ::1), private subnets, and link-local addresses are rejected** to prevent SSRF attacks. |
|     Port     |    Yes   | Database listener port. Common defaults: MySQL `3306`, PostgreSQL `5432`, ClickHouse `9000`.                                                                             |
|   Username   |    Yes   | The account used to connect. A read-only account is strongly recommended.                                                                                                |
|   Password   |    Yes   | Connection password, stored encrypted and never shown in plaintext after saving.                                                                                         |
|   Database   |    Yes   | Name of the database to connect to.                                                                                                                                      |
| Extra Params |    No    | Additional connection parameters as key-value pairs, mapped to driver-specific DSN options (e.g., MySQL `timeout`, PostgreSQL `sslmode`).                                |

### Query Configuration

|          Field          | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| :---------------------: | :------: | :-----: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|          Query          |    Yes   |    -    | A read-only SELECT statement (or a CTE starting with `WITH`). **DML/DDL keywords** (`INSERT`, `UPDATE`, `DELETE`, `DROP`, etc.) are **forbidden**. **Parameter placeholders** (`?`) are **forbidden**. **Row-limiting keywords** (`LIMIT`, `OFFSET`, `FETCH NEXT`, `SELECT TOP`) are **forbidden** — the system controls pagination automatically. The query output **must include** both cursor columns (`time_column` and `id_column`); validation fails at save time if they are missing. |
|    Timeout (seconds)    |    Yes   |   `5`   | Maximum execution time for a single page query, range `1 ~ 10` seconds. Values above `10` are clamped to `10`.                                                                                                                                                                                                                                                                                                                                                                               |
| Polling Cycle (seconds) |    Yes   |    -    | Interval between successive polling cycles (seconds).                                                                                                                                                                                                                                                                                                                                                                                                                                        |
|        Max Pages        |    Yes   |   `5`   | Maximum pages fetched per polling cycle, range `1 ~ 10`. Values above `10` are clamped to `10`. Pagination stops when the cap is reached or a page returns fewer rows than `page_size`.                                                                                                                                                                                                                                                                                                      |
|        Page Size        |    Yes   |  `500`  | Maximum rows returned per page, range `1 ~ 1000`. Values above `1000` are clamped to `1000`.                                                                                                                                                                                                                                                                                                                                                                                                 |

<Note>
  The total rows ingested per cycle is bounded by `max_pages × page_size` and is also subject to a system-wide event count ceiling. Set `page_size` to a batch size your database can handle efficiently (typically 200–500 rows) and tune `max_pages` based on your alert throughput.
</Note>

### Cursor Configuration

Flashduty uses **keyset pagination** to fetch new rows incrementally and avoid full-table scans. After each polling cycle, the system persists the cursor position of the last processed row and resumes from there on the next cycle.

|             Field             | Required | Description                                                                                                                                                                                                                                                            |
| :---------------------------: | :------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|  Time Column (`time_column`)  |    Yes   | The timestamp column used for ordering and pagination. The name must match `[a-zA-Z_][a-zA-Z0-9_]*` and must appear in the SELECT output.                                                                                                                              |
|    ID Column (`id_column`)    |    Yes   | A unique-identifier column used together with the time column to break ties when multiple rows share the same timestamp (typically an auto-increment primary key or UUID). Same naming rules as `time_column`.                                                         |
| Initial Time (`initial_time`) |    No    | The starting point for the very first fetch (or after a checkpoint reset), in `YYYY-MM-DD HH:MM:SS` format. If omitted, the system uses the time the integration was saved as the starting point — **rows that already exist before that moment will not be fetched**. |

**How pagination works**

The system wraps your SELECT statement in a subquery and appends keyset conditions, ordering, and a LIMIT on the outer query:

```sql theme={null}
SELECT * FROM (<your query>) AS _src
WHERE (_src.<time_column> > ? OR (_src.<time_column> = ? AND _src.<id_column> > ?))
ORDER BY _src.<time_column> ASC, _src.<id_column> ASC
LIMIT <page_size>
```

Do not include `ORDER BY`, `LIMIT`, or `OFFSET` in your own query — the system controls these.

**Automatic checkpoint reset**

The system computes a fingerprint over the fields that define the ordered row stream: `driver`, `host`, `port`, `database`, `query`, `time_column`, `id_column`, and `initial_time`. When any of these fields change, the checkpoint is automatically invalidated and the next fetch restarts from `initial_time`. Editing field mappings or severity mappings **does not** trigger a checkpoint reset and will not replay historical rows.

### Field Mapping

Field mapping controls which database columns (or constant values) are mapped to which fields of the Standard Alert Event.

#### Standard Field Mapping (fields)

Each mapping rule specifies a **target field** (a Standard Event field name) and a **rule**. Three rule types are supported:

| Rule Type | `type` Value | `value` Description                                                                                                                                                             |
| :-------: | :----------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|   Column  |   `column`   | Reads the value of the named column from the database row directly. Example: `"type": "column", "value": "alert_title"` reads the `alert_title` column.                         |
|  Constant |    `const`   | A fixed string applied to every row. Example: `"type": "const", "value": "Critical"` always sets the field to `Critical`.                                                       |
|  Template |  `template`  | A Go template string that can reference any column in the row. Example: `"type": "template", "value": "{{.host}}: {{.message}}"` concatenates the `host` and `message` columns. |

When the target field is `event_time`, the system automatically converts the column value to a Unix timestamp in seconds. Supported input formats include `time.Time`, Unix milliseconds (integer), `"2006-01-02T15:04:05"`, `"2006-01-02 15:04:05"`, and other common datetime representations.

#### Label Mapping (labels)

Label mapping controls how database columns are converted into the `labels` dictionary of the alert event. Four modes are supported:

|       Mode       |    `mode` Value   | Description                                                                                                                                                                                     |
| :--------------: | :---------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Specific Columns | `include_columns` | Only the columns listed in `columns` are included as labels. The label key is the column name; the label value is the column's string representation.                                           |
|    All Columns   |   `all_columns`   | Every column in the query result is automatically added as a label, excluding cursor columns and columns already referenced by `fields` rules. Useful when the column set is small or variable. |
|    JSON Column   |   `json_column`   | Parses the specified column's value as a JSON object (`{"key": "value", ...}`) and maps it directly to labels. The column value must be valid JSON.                                             |
|      Manual      |      `manual`     | Explicitly maps label keys to source column names, e.g. `{"region": "dc_region", "env": "deploy_env"}`. The map key is the label name; the map value is the source column name.                 |

### Severity Mapping

External systems use inconsistent field names and values for alert severity. **Severity Mapping** translates the external value into Flashduty's standard `Critical / Warning / Info`.

* Flashduty reads the `event_status` field produced by field mapping and looks it up in the severity mapping table.
* **Fallback when no match**: if `event_status` does not match any key in the table, the severity defaults to `Warning`.

Example configuration:

```json theme={null}
{
  "P0": "Critical",
  "P1": "Critical",
  "P2": "Warning",
  "P3": "Info"
}
```

## Default Route

***

When creating a "Shared Integration" (under **Integration Center => Alert Events**), you must configure a default route — otherwise newly pulled events will be dropped. After creation, you can add finer-grained rules under **Route**.

A "Dedicated Integration" (created under the **Integrations** tab of a specific channel) is automatically bound to that channel and does not require a separate default route.

## Notes

***

* **Database account permissions**: Create a dedicated read-only account for DB Pull with only `SELECT` privilege on the target table. Do not grant write access.
* **Network reachability**: The Flashduty service must be able to reach `host:port` over the public internet or a dedicated line. Private subnets (`10.x.x.x`, `192.168.x.x`, `172.16–31.x.x`) and loopback addresses are rejected by the system.
* **Index on cursor columns**: For performance, create a composite index on `(time_column, id_column)` to support efficient keyset pagination queries.
* **Time column precision**: The system records cursor timestamps with microsecond precision (`2006-01-02 15:04:05.999999`). Use at least millisecond precision for your time column (`DATETIME(3)` or `TIMESTAMP(6)`) to avoid missing rows when many rows arrive within the same second.
* **Query idempotency**: After a checkpoint reset, the same rows may be fetched again. Ensure your field mapping includes a column that uniquely identifies each alert (mapped to `alert_key`) so that Flashduty can correctly deduplicate events.
