Query icon
Fail icon
Log icon
If icon
Pause icon
Loop icon
SlackIncomingWebhook icon
Schedule icon

Purge expired Neo4j sessions in approved bounded batches

Purge expired Neo4j session and event nodes with approval, bounded transactions and protected graph checks.

Categories
DataInfrastructure

Safety

An expiry property is a deletion contract. A wrong unit or an overbroad policy can destroy live graph data. Use integer Unix seconds, review the policy and keep a restorable backup.

Guard What it stops
dry_run: true, auto_remediate: false and explicit approval An unattended run deleting data
Every node label and every attached relationship type must be allowed Removing business nodes or edges outside the retention contract
Fixed past cutoff, integer expiry and degree ceiling Future timestamps, malformed expiry and high-degree deletes
Whole-plan node budget and bounded transactions Silent truncation and an oversized delete transaction
Exact plan recheck after approval Applying an approval after nodes, expiry values or edges changed
Node property lock, approved edge subset and plain DELETE Deleting an extended node or silently detaching an unexpected edge
Recount, approved-id absence and protected census Reporting success with targets remaining or protected counts changed

How it works

  1. clock (io.kestra.plugin.neo4j.Query) fixes the cutoff from database time or a supplied past value. audit counts expired and eligible nodes per policy label. Missing, non-integer and equal-to-cutoff expiry values are retained.
  2. plan (io.kestra.plugin.neo4j.Query) records element ids, expiry values and sorted incident edge ids. All labels on a target must belong to retention_labels; all edges must use disposable_relationship_types. High-degree nodes are excluded. within_budget (io.kestra.plugin.core.execution.Fail) refuses a plan over max_nodes before approval or writes.
  3. purge_gate (io.kestra.plugin.core.flow.If) requires both mutation inputs. approval (io.kestra.plugin.core.flow.Pause) waits up to 20 minutes. approved_purge reads onResume.approved ?? false; fresh_plan and unchanged_plan refuse a changed snapshot.
  4. batches (io.kestra.plugin.core.flow.Loop, concurrencyLimit: 1) runs native delete_batch queries. Each transaction locks node expiry values before rechecking them. A batch whose target lost eligibility, changed expiry or gained an unexpected edge deletes nothing. Approved edges already removed by an earlier batch are allowed to be absent. The query explicitly deletes the captured disposable edges then uses plain node DELETE; it never uses DETACH DELETE.
  5. remaining, approved_remaining and protected_after (io.kestra.plugin.neo4j.Query) check eligible backlog, target absence and counts of labels and relationship types outside the policy. verified fails on a mismatch. failure_alert (io.kestra.plugin.slack.notifications.SlackIncomingWebhook) reports errors.

What you get

A repeatable expiry sweep with per-label counts, protected lookalikes and bounded irreversible changes. Expired nodes excluded by the policy remain in the report; a successful purge clears eligible nodes at the original cutoff.

Who it's for

Platform teams whose Neo4j session or event loader writes an expiry property and needs an approved retention sweep that respects the rest of the graph.

Why orchestrate this with Kestra

Kestra keeps the database snapshot, approval, native writes and verification in one execution history. The disabled nightly schedule runs with safe inputs. A flow concurrency limit serializes executions of this flow id.

Tested end to end

Kestra 2.0.6 OSS with bundled plugin-neo4j 1.6.0 and Neo4j Community 5.26.32. Independent cypher-shell reads measured 6 eligible expired nodes before remediation and 0 afterward, using batches of 2. All 28 retained nodes, their identities and properties and all 3 non-disposable relationships survived. The disposable edge count fell by 1.

Case Execution Result
healthy 30s49ymgn6g9uJtbeUlrHK SUCCESS
degraded_dry_run HokYIFVPIMENmykWpLJFf SUCCESS
declined_approval 7OyqCIz2M8cBmiOcNsB8cW SUCCESS
remediation 4XP0915Ahqa8KRb3V64MZp SUCCESS
repeat 7kLP23ZeAqDQNqStwuUKbN SUCCESS
ineffective_write 5ZthdnArumFx4bjJ26TgUf FAILED
race_or_partial 44PMyoOWTargdQUnWqADOg FAILED

The refused write held database locks until the native transaction failed. No target was removed. Further cases cover budget equality and excess, cutoff equality, degree boundaries, an empty graph, 1001 candidates, quoted Unicode identifiers, a concurrent execution held by the flow limit, changes during approval, a disconnected service, partial recovery and a repeat run. Healthy, remediation and stale-approval cases were repeated on a fresh fixture.

Prerequisites

  • Neo4j 5.26 Community reachable by the Kestra worker with the Neo4j plugin installed.
  • Complete read visibility of nodes, labels, expiry properties and incident relationships. Remediation also needs property writes for locks plus node and relationship deletes.
  • An integer Unix-seconds expiry contract and an explicit policy for disposable labels and relationship types. Names are case-sensitive; commas delimit policy entries and cannot occur inside a name.
  • A restorable backup and server transaction/lock timeouts suitable for the maintenance window. The native tasks have a one-minute task timeout and no configured write retries.

Secrets

Secret Purpose
NEO4J_URL Default Bolt endpoint
NEO4J_PASSWORD Default Neo4j credential
SLACK_WEBHOOK_URL Default failure webhook through secret('SLACK_WEBHOOK_URL')

Inputs

Input Default Purpose
neo4j_url, neo4j_username, neo4j_password URL secret, neo4j, password secret Connection and complete graph visibility
retention_labels Session,Event All labels allowed on a deletable node
expiry_property expires_at Integer Unix seconds, strictly below cutoff
disposable_relationship_types NEXT_EVENT Edge types explicitly allowed to be deleted
cutoff_epoch 0 Database time or an explicit past cutoff
max_nodes 500 Whole-plan budget, at most 1000
batch_size 50 Nodes per transaction, at most 100
max_degree 10 Incident edge ceiling per target, at most 20
auto_remediate, dry_run false, true Both must permit mutation
slack_webhook_url Slack secret Failure webhook

Quick start

  1. Configure the secrets and minimum database permissions.
  2. Set the labels, expiry property and disposable relationship types to your retention contract. Run with the safe defaults and inspect retention_report.
  3. Back up the graph. Run with auto_remediate: true and dry_run: false, review the snapshot then approve the Pause.
  4. Read the final per-label counts and protected census. If the run fails, inspect committed batches and run detection again before approving a new plan.
  5. Enable the nightly trigger only after reviewing its safe-mode inputs.

How to extend

Add an export-and-restore procedure before deletion if retention requires an archive. For a large graph, replace the dynamic scan with explicit indexed label/property branches and repeat the detector and race cases before increasing budgets.

Limits: this is a logical retention purge. The dynamic queries scan the graph; node budgets bound selected output and writes, not scan cost. It does not compact store files or prove disk-space, cache or latency gains. Missing, malformed, future, non-disposable and high-degree records remain. Cross-flow copies and application writers have separate concurrency scopes. Concurrent unrelated changes can fail protected-census verification after earlier batches committed. A failed or interrupted run keeps completed transactions; the next run plans remaining eligible records without restoring deleted data. Use server timeouts to bound database work as well as the task timeout. The neo4j.Query task of plugin-neo4j 1.4.9 (bundled with Kestra 2.0.5) has no database or parameters property, so the queries run on the account's home database and the label, property and relationship names are placed in the Cypher text as escaped string literals. Set the home database of the account if the data is not in the default one.

Links

This blueprint was created by zkasuran.

See How

New to Kestra?

Use blueprints to kickstart your first workflows.