PolicySync Changelog Slow-Task Diagnostics¶
PolicySync includes opt-in, off-by-default diagnostics that help find the cause when a changelog occasionally takes far longer to process than its work warrants (for example, a single resource task that appears to hang). The diagnostics are part of the shared PolicySync changelog path, so they apply to every connector.
When enabled, the connector captures — on its own, without anyone attaching a debugger:
- a slow-task WARN (to a dedicated file) naming the resource and the per-phase millisecond breakdown of any changelog resource task that runs longer than a threshold, and
- an automatic in-process thread dump when a changelog's processing exceeds a threshold, so the stuck thread's stack is recorded while it is still stuck.
Both features are disabled by default (their thresholds are 0). On upgrade nothing changes and nothing is written; enabling them is an explicit operator decision, and the per-task overhead when disabled is a single comparison.
Note
The slow-task WARN and the thread dump only observe — a failure in either is caught and swallowed and can never change changelog processing or its outcome. The resource-process task time-box (policysync.changelog.resource.task.timeout.ms, off by default) is the exception: when you enable it, it deliberately changes processing — it abandons a task that overruns its processing budget (measured from when the task starts running) or its queue budget (policysync.changelog.resource.task.queue.timeout.ms, measured from submit) and releases the barrier so one stuck task cannot stall the whole changelog, and once retries are exhausted it marks the task failed with a RESOURCE_PROCESS_TASK_TIMEOUT audit.
Configuration¶
Set these in Privacera Manager. The primary thresholds — the slow-task WARN, the thread dump, and the resource-task time-box — all default to 0 (disabled) for every connector. You can enable them globally (set the variable so all connectors pick it up) or per connector instance — set the variable in that connector instance's custom vars (config/custom-vars/connectors/<connector>/<instance>/), which overrides the global default for that instance only. For example, enable the thread dump on the Databricks Unity Catalog instance while every other connector (Lake Formation, Snowflake, and so on) stays off.
| Privacera Manager variable | PolicySync property | Default | Meaning |
|---|---|---|---|
CONNECTOR_CHANGELOG_SLOW_RESOURCE_TASK_WARN_MS | policysync.changelog.slow.resource.task.warn.ms | 0 (off) | Emit a WARN with the per-phase breakdown for any changelog resource task that runs at least this many ms. 0 disables. Recommended when enabling: 30000. |
CONNECTOR_CHANGELOG_SLOW_CHANGELOG_DUMP_MS | policysync.changelog.slow.changelog.dump.ms | 0 (off) | Capture an automatic thread dump when a changelog's processing takes at least this many ms. 0 disables. Recommended when enabling: 600000 (10 min). |
CONNECTOR_CHANGELOG_STALL_DUMP_COUNT | policysync.changelog.stall.dump.count | 3 | Number of thread dumps captured per trigger. |
CONNECTOR_CHANGELOG_STALL_DUMP_INTERVAL_MS | policysync.changelog.stall.dump.interval.ms | 30000 | Interval (ms) between the dumps of a single trigger (30 s). |
CONNECTOR_CHANGELOG_STALL_DUMP_RETENTION_HOURS | policysync.changelog.stall.dump.retention.hours | 24 | Age bound (hours) for the dump/ directory; older files are evicted. |
CONNECTOR_CHANGELOG_STALL_DUMP_MAX_TOTAL_MB | policysync.changelog.stall.dump.max.total.mb | 256 | Cap on total dump/ size (MB); oldest-first eviction. |
CONNECTOR_CHANGELOG_STALL_DUMP_MAX_FILES | policysync.changelog.stall.dump.max.files | 100 | Cap on dump/ file count (so dumps do not grow without bound); oldest-first eviction. |
CONNECTOR_CHANGELOG_STALL_DUMP_MIN_GAP_MS | policysync.changelog.stall.dump.min.gap.ms | 60000 | Minimum gap (ms) between dump triggers, to rate-limit captures. |
CONNECTOR_CHANGELOG_RESOURCE_TASK_TIMEOUT_MS | policysync.changelog.resource.task.timeout.ms | 0 (off) | Processing budget (ms) for a changelog resource task, measured from when it starts running (queue wait excluded). A running task that overruns it is abandoned as stuck (best-effort cancel — not a hard kill), logged to the slow-task file, and the barrier is released so one stuck task does not block the whole changelog. 0 disables the whole time-box. Recommended when enabling: 300000 (5 min). |
CONNECTOR_CHANGELOG_RESOURCE_TASK_QUEUE_TIMEOUT_MS | policysync.changelog.resource.task.queue.timeout.ms | 0 (no queue bound) | Queue budget (ms), measured from submit. A task that never gets a pool thread within it is abandoned as abandonedWhileQueued — the safety valve that still releases the barrier when the pool is saturated by stuck threads. 0 leaves queued tasks unbounded (only safe when the pool reliably drains). Recommended when enabling: 3600000 (60 min), sized above the expected queue-drain ≈ (impacted resources ÷ pool threads) × per-task time. |
CONNECTOR_CHANGELOG_RESOURCE_TASK_MAX_RETRIES | policysync.changelog.resource.task.max.retries | 0 | Number of times a timed-out resource task is re-submitted before it is marked failed (with a RESOURCE_PROCESS_TASK_TIMEOUT audit). A retry does not free the stuck thread, so keep this count small (recommended 1); 0 = no retry. |
The six stall.dump.* properties only take effect when the thread dump is enabled. The three resource.task.* properties are the independent resource-process task time-box, which is active only when policysync.changelog.resource.task.timeout.ms is non-zero. It uses two separate budgets — a processing budget measured from run-start (so a task merely waiting its turn in a deep queue is never abandoned as if it were stuck) and a queue budget measured from submit (the safety valve that releases the barrier when the pool is jammed). Size the queue budget above the expected queue depth when a small pool fans out many resource tasks.
When to enable¶
Enable these when a changelog is reported to be intermittently slow and you need to catch it in the act — the slowness is usually random, so attaching a debugger after the fact rarely works. Because everything is off by default, you turn on only what you need and turn it back off once you have collected the evidence.
Operator runbook¶
-
Enable the diagnostics. In Privacera Manager, set the thresholds to the recommended values, then run the Privacera Manager update so the connector picks up the new
policysync.properties:YAML Optionally, to also time-box an over-running resource task (abandon + log, release the barrier, and emit a
RESOURCE_PROCESS_TASK_TIMEOUTaudit after retries): -
Reproduce, or wait for recurrence. Let the connector run until a slow changelog occurs again.
-
Collect the output from the connector pod. The diagnostics write to the connector's log directory:
changelog-slow-task.log— one WARN line per slow resource task, with its resource key, resource type, principal count, and the per-phase millisecond breakdown. This file is written by a dedicated, file-only logger and is not sent to the console or logstash appenders.dump/<YYYY-MM-DD>/<changeLogId>/— the captured thread dumps (dump-00.txt, and so on) plus asummaryfile listing the in-flight resource keys and the step each was executing when the dump was taken.
Bash See Capturing a Thread Dump and Collecting Support Files for copying these files off the pod along with
policysync.properties/connector.properties. -
Interpret the output. The WARN's per-phase breakdown tells you which phase of the task was slow; the
summaryfile names the step each in-flight task was on; and the thread dumps show the exact stack of the stuck thread. Together these localize where the time went. -
Disable when done. Set the thresholds back to
0and re-apply (include the time-box'sCONNECTOR_CHANGELOG_RESOURCE_TASK_TIMEOUT_MSandCONNECTOR_CHANGELOG_RESOURCE_TASK_QUEUE_TIMEOUT_MSif you enabled them):
Sensitive content
The WARN line and the dump summary contain resource identifiers (catalog / schema / table names), and are written to pod-local disk only. They are never shipped off the pod automatically. Treat the collected files as customer-identifying and mask as needed before sharing. Principal information is recorded as counts only, never names.
- Prev topic: Troubleshooting
- Related: Capturing a Thread Dump