# Real-Time Indexing Backlog

> This alert triggers when a record's Real-Time Indexing queue in PTSF_RTISTG holds too many index updates or its oldest update has waited too long.

---

Site [llms.txt](/llms.txt)

---

<div class="alert alert-warning border shadow-sm mb-4 psl-prospect-cta" role="note">
  <div class="d-flex flex-column flex-lg-row align-items-lg-center justify-content-between gap-3">
    <div>
      <strong>New to psLens?</strong> This page documents one specific alert. To see how it appears on the dashboard,
      what operators investigate, and how teams tune it, start with a live walkthrough.
    </div>
    <div class="d-flex flex-wrap gap-2">
      <a class="btn btn-sm btn-primary" href="/contact/">Book a Demo</a>
      <a class="btn btn-sm btn-outline-primary" href="/docs/alerts/">Browse Alerts</a>
    </div>
  </div>
</div><div id="pslens-context-panel" class="card border-info mb-4 d-none">
  <div class="card-header bg-light text-info py-2 fw-bold d-flex align-items-center border-bottom border-info-subtle">
    <i class="bi bi-info-circle-fill me-2"></i>
    <span>Tailored Operational Context</span>
  </div>
  <div class="card-body p-0">
    <ul class="list-group list-group-flush">
      <li id="row-db" class="list-group-item d-flex align-items-center justify-content-between py-2 d-none">
        <strong>Target Database:</strong>
        <span id="ctx-db" class="badge bg-secondary font-monospace">&mdash;</span>
      </li>
      <li id="row-type" class="list-group-item d-flex align-items-center justify-content-between py-2 d-none">
        <strong>Context Type:</strong>
        <span id="ctx-type" class="badge bg-light text-dark border font-monospace text-uppercase">&mdash;</span>
      </li>
      <li id="row-severity" class="list-group-item d-flex align-items-center justify-content-between py-2 d-none">
        <strong>Alert Severity:</strong>
        <span id="ctx-severity" class="badge">&mdash;</span>
      </li>
      <li id="row-time" class="list-group-item d-flex align-items-center justify-content-between py-2 d-none">
        <strong>Triggered Time:</strong>
        <span id="ctx-time" class="text-muted small">&mdash;</span>
      </li>
      <li id="row-details" class="list-group-item py-2 d-none">
        <strong id="label-details" class="d-block mb-1">Firing Context:</strong>
        <code id="ctx-details" class="d-block p-2 bg-light border rounded small"
          style="white-space: pre-wrap; word-break: break-all;">&mdash;</code>
      </li>
      <li id="row-sql" class="list-group-item py-2 d-none">
        <div class="d-flex justify-content-between align-items-center mb-1">
          <strong id="label-sql">Contextual SQL:</strong>
          <button id="btn-copy-sql" type="button" class="btn btn-sm btn-outline-secondary py-0 px-2 small">
            <i class="bi bi-clipboard me-1"></i>Copy SQL
          </button>
        </div>
        <pre class="mb-0 p-2 bg-light border rounded small"><code id="ctx-sql" style="white-space: pre-wrap; word-break: break-word;">&mdash;</code></pre>
      </li>
    </ul>
  </div>
</div>

<script>
  (function () {
    const params = new URLSearchParams(window.location.search);
    const metadata = params.get('metadata');
    if (!metadata) return;

    try {
      
      const base64 = metadata.replace(/-/g, '+').replace(/_/g, '/');
      const jsonStr = decodeURIComponent(escape(window.atob(base64)));
      const data = JSON.parse(jsonStr);

      if (data) {
        let hasData = false;

        if (data.db) {
          document.getElementById('ctx-db').textContent = data.db;
          document.getElementById('row-db').classList.remove('d-none');
          hasData = true;
        }

        if (data.type) {
          document.getElementById('ctx-type').textContent = data.type;
          document.getElementById('row-type').classList.remove('d-none');
          hasData = true;
        }

        if (data.severity) {
          const severityBadge = document.getElementById('ctx-severity');
          const severity = data.severity.toLowerCase();
          severityBadge.textContent = severity.toUpperCase();
          if (severity === 'critical') {
            severityBadge.className = 'badge bg-danger';
          } else if (severity === 'warning') {
            severityBadge.className = 'badge bg-warning text-dark';
          } else {
            severityBadge.className = 'badge bg-info';
          }
          document.getElementById('row-severity').classList.remove('d-none');
          hasData = true;
        }

        if (data.t) {
          const date = new Date(data.t * 1000);
          document.getElementById('ctx-time').textContent = date.toLocaleString();
          document.getElementById('row-time').classList.remove('d-none');
          hasData = true;
        }

        if (data.details) {
          document.getElementById('ctx-details').textContent = data.details;

          
          const labelDetails = document.getElementById('label-details');
          if (data.type === 'object') {
            labelDetails.textContent = 'Object Metadata Details:';
          } else if (data.type === 'report') {
            labelDetails.textContent = 'Report Description:';
          } else {
            labelDetails.textContent = 'Firing Context:';
          }

          document.getElementById('row-details').classList.remove('d-none');
          hasData = true;
        }

        if (data.sql) {
          document.getElementById('ctx-sql').textContent = data.sql;
          document.getElementById('row-sql').classList.remove('d-none');
          const copyBtn = document.getElementById('btn-copy-sql');
          if (copyBtn && navigator.clipboard) {
            copyBtn.addEventListener('click', function () {
              navigator.clipboard.writeText(data.sql).then(function () {
                copyBtn.innerHTML = '<i class="bi bi-check2 me-1"></i>Copied';
                setTimeout(function () {
                  copyBtn.innerHTML = '<i class="bi bi-clipboard me-1"></i>Copy SQL';
                }, 2000);
              });
            });
          }
          hasData = true;
        }

        if (hasData) {
          document.getElementById('pslens-context-panel').classList.remove('d-none');
        }
      }
    } catch (e) {
      console.error('Failed to parse operational context metadata:', e);
    }
  })();
</script>

## Real-Time Indexing Backlog Alert

**Alert ID:** `rti_staging_backlog`
**Category:** Search Framework
**Default thresholds:** 1,000 queued updates, or 30 minutes
**Runs:** every 15 minutes

### What This Alert Detects

With Real-Time Indexing on, a database trigger on each indexed record writes a row to the queue table `PTSF_RTISTG` for every insert, update, and delete. PeopleSoft then sends those rows to the search engine and removes them. This alert triggers when the queue for a record is not draining.

Two situations cause it:

- **The process that drains the queue has stopped.** Rows keep arriving and none leave, so the oldest row gets older.
- **A bulk update or batch load changed thousands of rows** on a record that carries a Real-Time Indexing trigger. The queue fills faster than it drains.

### Severity Logic

|                         Condition                         | Severity |
| --------------------------------------------------------- | -------- |
| A record has more than `thresholdCount` queued updates    | Warning  |
| A record's oldest queued update is `thresholdMinutes` old | Warning  |

Either condition triggers the alert. There is one alert item per record.

### What Gets Checked

The alert runs one aggregate query on `PTSF_RTISTG`, grouped by `RECNAME`, returning the row count and the oldest `PTRTIINSERTDTTM` per record. The age is measured against the database server's clock.

### Alert Details

Each alert item includes:

- The record whose trigger queued the updates (`RECNAME`)
- The number of queued updates
- When the oldest update was queued, and how many minutes ago
- A link to the Search Framework Monitor

### Configuration

```yaml
alerts:
  checks:
    rti_staging_backlog:
      enabled: true
      thresholdCount: 1000    # Queued updates per record before alerting
      thresholdMinutes: 30    # Age of the oldest queued update before alerting
```

|      Setting       | Default |                         Description                         |
| ------------------ | ------- | ----------------------------------------------------------- |
| `thresholdCount`   | `1000`  | Queued updates on one record that trigger the alert         |
| `thresholdMinutes` | `30`    | Age in minutes of the oldest queued update that triggers it |

### How to Respond

1. Open the [Search Framework Monitor](/docs/monitor/search-framework/) and switch on the **Real-Time Indexing Queue** panel to see the oldest queued rows and their keys.
2. If the count is high and the oldest row is recent, a bulk change is in progress. Check Process Monitor for the batch job, and confirm the queue shrinks once it finishes.
3. If the oldest row keeps getting older, the queue is not draining. Check that the Real-Time Indexing server process is running in the application server domain and that the search engine is reachable.
4. Open the record's detail page and switch on **Search Framework Usage** to see which search definitions index it.

### Required Table

`PTSF_RTISTG`. Without it the alert is inactive.
