Visitar URL original
Drain JDBC realm time-series data via a batched purge command by ayushtkn · Pull Request #5089 · apache/polaris · GitHub
Skip to content

Drain JDBC realm time-series data via a batched purge command - #5089

Open
ayushtkn wants to merge 1 commit into
apache:mainfrom
ayushtkn:deleteAll
Open

ayushtkn wants to merge 1 commit into
apache:mainfrom
ayushtkn:deleteAll

Conversation

@ayushtkn

@ayushtkn ayushtkn commented Jul 18, 2026 •

Copy link
Copy Markdown
Member

deleteAll (a BasePersistence operation) previously left a realm's time-series data — events, scan_metrics_report, commit_metrics_report — behind. Rather than fix that with a broad inline delete, this makes the time-series purge asynchronous, queued, and batched, matching somewhat the behavior of the NoSQL maintenance command.

deleteAll no longer touches time-series tables. It atomically enqueues the realm into a new realm_purge marker table, as the last statement in its own transaction — so a purged realm is never left with orphaned time-series data, and the marker never commits without the metastore delete.

  • A new operator-run admin command, purge-drain, processes the queue: it claims each realm under a lease (one active drainer per realm across nodes, via a conditional UPDATE), deletes its rows in bounded batches, then clears the marker.

A realm whose drain fails or is interrupted keeps its marker and is retried once its lease expires. It's safe to run repeatedly/concurrently and is meant to be scheduled periodically (e.g. once a day), like nosql maintenance-run.

Per-table purgers are discovered via CDI (Instance<RealmDataPurger>), so adding a table means adding a producer, not editing the drainer.

Checklist

  • 🛡️ Don't disclose security issues! (contact security@apache.org)
  • 🔗 Clearly explained why the changes are needed, or linked related issues: Fixes #
  • 🧪 Added/updated tests with good coverage, or manually tested (and explained how)
  • 💡 Added comments for complex logic
  • 🧾 Updated CHANGELOG.md (if needed)
  • 📚 Updated documentation in site/content/in-dev/unreleased (if needed)

Copilot AI review requested due to automatic review settings July 18, 2026 08:02
@github-project-automation github-project-automation Bot moved this to PRs In Progress in Basic Kanban Board Jul 18, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extends JDBC metastore cleanup so JdbcBasePersistenceImpl.deleteAll() purges additional realm-scoped tables introduced in newer schema versions (events in v3+, scan/commit metrics report tables in v4+), preventing stale data from remaining after a realm-scoped delete.

Changes:

  • Update JdbcBasePersistenceImpl.deleteAll() to delete from events, scan_metrics_report, and commit_metrics_report when the schema version supports them.
  • Add a new integration-style test that populates all realm-scoped tables in schema v5 and verifies deleteAll() purges only the target realm.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
persistence/relational-jdbc/src/main/java/org/apache/polaris/persistence/relational/jdbc/JdbcBasePersistenceImpl.java Extends deleteAll() to delete newer realm-scoped tables, gated by schema version.
persistence/relational-jdbc/src/test/java/org/apache/polaris/persistence/relational/jdbc/JdbcBasePersistenceImplTest.java Adds coverage verifying realm-scoped purge behavior across all relevant tables for schema v5.

Comment on lines +413 to +432
if (schemaVersion >= 3) {
datasourceOperations.execute(
connection,
QueryGenerator.generateDeleteQuery(
ModelEvent.ALL_COLUMNS, ModelEvent.TABLE_NAME, params));
}
if (schemaVersion >= 4) {
datasourceOperations.execute(
connection,
QueryGenerator.generateDeleteQuery(
ModelScanMetricsReport.ALL_COLUMNS,
ModelScanMetricsReport.TABLE_NAME,
params));
datasourceOperations.execute(
connection,
QueryGenerator.generateDeleteQuery(
ModelCommitMetricsReport.ALL_COLUMNS,
ModelCommitMetricsReport.TABLE_NAME,
params));
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sense. added tests for all versions

@dimas-b dimas-b left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for bringing this up, @ayushtkn ! I think it's a good area to improve, but I'm not sure about using a simple DELETE SQL for that 🤔

connection,
QueryGenerator.generateDeleteQuery(
ModelScanMetricsReport.ALL_COLUMNS,
ModelScanMetricsReport.TABLE_NAME,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The javadoc on deleteAll mentions only "entity and grant record metadata".

deleteAll() is specified by BasePersistence, so I do not think it applies to events and metrics, which are under MetricsPersistence. It's currently detached from BasePersistence in terms of interfaces and its implementation is moving to extensions in #5068 🤔

I believe purging metric should be done under a separate method call and probably after merging #5068.

IMHO, conceptually, events should also not be bound to BasePersistence either. I'd like to isolate them link metrics and then add dedicated maintenance tasks for purging (see my other comment).

WDYT?

datasourceOperations.execute(
connection,
QueryGenerator.generateDeleteQuery(
ModelEvent.ALL_COLUMNS, ModelEvent.TABLE_NAME, params));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Events a time-series type of data. There may be a lot more of them in the database than Meta Store data. I'm not sure issuing a broad DELETE for them in one Tx is a robust approach.

I tend to think it would be preferable to handle their purge asynchronously and in a retriable / staged manner similar to NoSQL maintenance: https://github.com/apache/polaris/blob/apache-polaris-1.6.0/persistence/nosql/persistence/maintenance/api/src/main/java/org/apache/polaris/persistence/nosql/maintenance/api/package-info.java

@ayushtkn

ayushtkn commented Jul 19, 2026 •

Copy link
Copy Markdown
Member Author

Thanks @dimas-b — agreed. We shouldn’t extend deleteAll() with sync DELETEs for events and metrics; that doesn’t match where this is heading with #5068 and the NoSQL maintenance model for time-series data.

I’ll revisit after #5068 and the linked PRs land, once metrics are decoupled from JdbcBasePersistenceImpl and we have a clearer place to hook purge in. From what I’ve looked at so far, doing JDBC realm purge async/staged like NoSQL would be a much larger effort (preserve realm state, maintenance command, batched deletes, etc.). I only did a quick pass on the NoSQL side — there may be a shortcut, refactor, or constraint I overlooked :-)

Thanks — I’ll park this for now and circle back.

@ayushtkn ayushtkn changed the title Fix JDBC deleteAll not purging events and metrics report tables Drain JDBC realm time-series data via a batched purge command Jul 21, 2026
@ayushtkn

Copy link
Copy Markdown
Member Author

Hi @dimas-b
I have changed the approach and built this on top of the #5068. do you have any feedback/suggestions around the approach, if this direction looks good or should add or do something differently

@github-actions

Copy link
Copy Markdown

This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions github-actions Bot added the stale label Aug 24, 2026
@ayushtkn

Copy link
Copy Markdown
Member Author

Not stale

@github-actions github-actions Bot removed the stale label Aug 25, 2026

@flyingImer flyingImer left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The async batching direction makes sense, but cleanup needs to remain safe across schema and realm lifecycle transitions.

params));
// Queue time-series data (events, metrics) for async batched purge instead of
// deleting inline. realm_purge exists from schema v5 only.
if (schemaVersion >= 5) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I didn't miss any context, this needs a migration path for existing databases and support in the current bootstrap schema. Existing v5 databases and the documented v3/v4-to-v5 upgrade do not create realm_purge, so the new enqueue fails and rolls back purge. Current main also bootstraps v6, whose scripts do not create this table. Could we install the queue on both paths and cover them with upgrade and fresh-bootstrap tests?

String sql =
"DELETE FROM "
+ fqTable
+ " WHERE realm_id = ? AND "

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, this can also delete events and metrics from a realm bootstrapped after the purge. bootstrapRealms does not check the pending marker, so purge R, bootstrap R, new writes, then purge-drain reaches this DELETE with the same realm_id.

If so, could we preserve the new realm's data, either by preventing bootstrap while cleanup is outstanding or by scoping the task to the retired realm incarnation? A regression test for that sequence would make the lifecycle guarantee explicit IMO

long staleClaimBeforeMs = nowMs - claimLeaseMs;
int drained = 0;
for (String realmId : store.listClaimable(staleClaimBeforeMs)) {
if (!store.claim(realmId, node, nowMs, staleClaimBeforeMs)) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, this lease does not enforce the documented one-drainer-per-realm guarantee. nowMs is reused for the whole run and the batch loop never renews or checks ownership, so another node can take an expired claim while the first keeps deleting, duplicating database work. The first node can then call complete, which removes the marker without checking the current owner. Could we use fresh claim times and retain ownership checks through draining and completion, with an expiry/takeover test?

@github-actions

Copy link
Copy Markdown

This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@ayushtkn

ayushtkn commented Nov 9, 2026

Copy link
Copy Markdown
Member Author

Have rebased the PR and resolved conflicts & have addressed the review comments

@orca-review-bot orca-review-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice rework. The lease/renew/owner-gated complete flow and the requested_at cutoff hold up well, and the H2 tests cover the tricky takeover paths.

One real hole: purging the same realm name twice before a drain finishes orphans the data written in between (see inline on RealmPurgeStore). Also inline: the missing events index and the new core SPI method.

Test gaps not tied to a line:

  • a purge→bootstrap→write→purge→drain case in RealmPurgeTest
  • PurgeDrainCommandTestBase only checks the output string. Can it write some events first and assert they're gone, so the producers are verified on PG/CRDB?
  • nothing exercises a v6 DB upgraded with the CHANGELOG DDL

The PR checklist is also still blank. Security-wise I see nothing new: the command is operator-only and all values are bound parameters.

🤖 AI-generated review comment — agents make mistakes, so please verify before acting on it.

String sql =
"INSERT INTO "
+ TABLE
+ " (realm_id, requested_at, status) VALUES (?, ?, 'pending') ON CONFLICT DO NOTHING";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a realm name is purged, re-bootstrapped, used, and purged again before the drain runs, this keeps the first requested_at. The drain only deletes rows older than that first purge. Then complete drops the marker, so whatever the second incarnation wrote is orphaned for good, which is the bug this PR set out to fix. Can we make this an upsert that moves requested_at forward and resets status to pending? And add a test for purge→bootstrap→write→purge→drain?

🤖 AI-generated review comment — agents make mistakes, so please verify before acting on it.

claimed_by TEXT,
claimed_at BIGINT,
PRIMARY KEY (realm_id)
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

events only has a primary key on event_id. Unlike the two metrics tables, it has no (realm_id, timestamp_ms) index, so every purgeBatch subquery on events can scan the whole table, and the drain repeats that once per batch. Since v7 is already a required migration, can we add CREATE INDEX IF NOT EXISTS ... ON events(realm_id, timestamp_ms) here (and in the CRDB/H2 scripts and the CHANGELOG DDL)?

🤖 AI-generated review comment — agents make mistakes, so please verify before acting on it.

* @param claimLeaseMs how long a drain claim is honored before another node may resume it
* @return the number of realms fully drained during this run
*/
default int drainRealmPurges(int batchSize, long claimLeaseMs) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds a method to the core MetaStoreManagerFactory SPI that only the JDBC backend implements. NoSQL already keeps its maintenance behind a backend-specific nosql subcommand. Could purge-drain be a JDBC-scoped subcommand the same way, or has this interface change been discussed on the dev list?

🤖 AI-generated review comment — agents make mistakes, so please verify before acting on it.

}

/** Records that {@code realmId}'s time-series data should be purged asynchronously. */
public void enqueue(String realmId, long nowMs) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Production code never calls enqueue, because deleteAll goes straight to enqueueInTransaction. Same with RealmDataPurger.storeName(), which nothing calls. Can we drop them, or use storeName in the failure warning so you can tell which table broke?

🤖 AI-generated review comment — agents make mistakes, so please verify before acting on it.

[v5-crd]: https://github.com/apache/polaris/blob/apache-polaris-1.7.0/persistence/relational-jdbc/src/main/resources/cockroachdb/schema-v5.sql
[v6-pg]: https://github.com/apache/polaris/blob/apache-polaris-1.8.0/persistence/relational-jdbc/src/main/resources/postgres/schema-v6.sql
[v6-crd]: https://github.com/apache/polaris/blob/apache-polaris-1.8.0/persistence/relational-jdbc/src/main/resources/cockroachdb/schema-v6.sql
[v7-pg]: https://github.com/apache/polaris/blob/apache-polaris-1.9.0/persistence/relational-jdbc/src/main/resources/postgres/schema.sql

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v4–v6 link to schema-vN.sql snapshots, but these point at schema.sql on the 1.9.0 tag. Is a schema-v7.sql snapshot planned at release, or should these links change?

🤖 AI-generated review comment — agents make mistakes, so please verify before acting on it.

@dimas-b dimas-b left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @ayushtkn ! Sorry about the delay.

Nice feature overall! Admin Tool stuff LGTM.

I have some comments about JDBC-specific impl.

);

-- Supports the realm purge drain, which deletes a realm's events older than a cutoff.
CREATE INDEX IF NOT EXISTS idx_events_realm_timestamp ON events (realm_id, timestamp_ms);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we only use it for purge, do we care about ordered deletes? Could we select a few PKs then delete them in a batch?.. or just do repeated DELTEs with a LIMIT?

I know it's overhead, but for purging it's probably manageable (async). On the other hand maintaining an index that is only used for purging will incur overhead in ordinary writes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point — dropped. The index is gone from all three schema.sql scripts and from the v7 migration DDL, and the drain may sequential-scan events.

On the alternatives: the current statement already selects PKs and deletes them in a batch — the subquery is a PK select with a LIMIT, in one round-trip (DELETE ... LIMIT isn't available on PostgreSQL, hence that shape). So the cost isn't ordering, it's finding the realm's rows, and that now falls on an operator-scheduled background command rather than any request path. Seems the right trade.

Comment thread CHANGELOG.md Outdated
Polaris. It adds the `realm_purge` table, which records realms whose time-series data is still
pending deletion, and an index on `events (realm_id, timestamp_ms)` that the drain relies on:
```sql
CREATE TABLE IF NOT EXISTS polaris_schema.realm_purge (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be preferable to keep actual upgrade SQL in https://polaris.apache.org/releases/1.8.0/metastores/relational-jdbc/#schema-upgrades ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes — and DatasourceOperations spells out exactly that: step 3 is "add the corresponding migration SQL to the Relational JDBC metastore documentation", step 4 "add a note to the changelog". I had it backwards.

Moved: the metastore docs now have a "Migrating to schema v7" section under Schema Upgrades with the DDL, and the CHANGELOG is back to a note linking there.

dimas-b
dimas-b previously approved these changes Nov 12, 2026

@dimas-b dimas-b left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with merging this PR "as is", but leaving some usability comments, in case you might also find them reasonable 🙂

Comment on lines +68 to +72
+ TIMESTAMP_COLUMN
+ " < ? LIMIT ?)";
try {
return datasourceOperations.executeUpdate(
new PreparedQuery(sql, List.of(realmId, realmId, cutoffMs, batchSize)));

@dimas-b dimas-b Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timestamp relates to the data in the row (e.g. an event), but the cutoffMs relates to the wall clock time of the purge request. These values may not be logically comparable to each other 🤔

That said, I think in most cases this logic should work fine.

return EXIT_CODE_USAGE;
}
int drained =
realmPurgeService.drainOnce(RealmPurgeService.localNodeId(), batchSize, claimLeaseMs);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently localNodeId is rather random. It does not help in identifying the runner.

WDYT about using ProcessHandle.current().pid() instead or in addition to the UUID in the context of the Admin Tool?

Also, it does not feel like localNodeId() belongs in RealmPurgeService. It might as well be a local method here 🤔

return EXIT_CODE_USAGE;
}
int drained =
realmPurgeService.drainOnce(RealmPurgeService.localNodeId(), batchSize, claimLeaseMs);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess a --realm option might be convenient to focus the action at user's discretion. WDYT?

}
anyRemaining = false;
for (RealmDataPurger purger : purgers) {
if (purger.purgeBatch(realmId, pending.requestedAtMs(), batchSize) > 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to report progress back to CLI 🤔

while (anyRemaining) {
if (!store.renew(realmId, node, clock.millis())) {
LOGGER.info(
"Stopped draining realm {} for purge: the claim is now held by another drainer",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When invoked from CLI, this message probably needs to do to the console 🤔

while (anyRemaining) {
if (!store.renew(realmId, node, clock.millis())) {
LOGGER.info(
"Stopped draining realm {} for purge: the claim is now held by another drainer",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we report the other node ID?

Comment on lines +67 to +72
if (!JDBC_PERSISTENCE_TYPE.equals(persistenceConfiguration.type())) {
spec.commandLine()
.getErr()
.printf(
"purge-drain applies to the '%s' persistence type, but '%s' is configured.%n",
JDBC_PERSISTENCE_TYPE, persistenceConfiguration.type());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tangential: Persistence type is only loosely coupled to metrics and events persistence. Ideally I think the can use different storage technology than the "Meta Store". In that case NoSQL MetaStore could be combined with JDBC Metrics persistence.

This is just a point for discussion. No need to change anything right now 😉

compileOnly("com.fasterxml.jackson.core:jackson-annotations")

runtimeOnly(project(":polaris-relational-jdbc"))
implementation(project(":polaris-relational-jdbc"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: #3947

@github-project-automation github-project-automation Bot moved this from PRs In Progress to Ready to merge in Basic Kanban Board Nov 12, 2026

-- Queue of realms whose time-series data (events, metrics reports) is pending asynchronous,
-- batched deletion by the background purge drainer. One row per realm being purged.
CREATE TABLE IF NOT EXISTS realm_purge (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess schema changes are similar to API spec changes, so it would be nice to have a dev discussion about this, if only for community awareness. WDYT?

return new Outcome(false, rows);
}
anyRemaining = false;
for (RealmDataPurger purger : purgers) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once a purger returns 0 for this realm it stays at 0 (the cutoff is fixed), but it's still called every round. Since events has no (realm_id, timestamp_ms) index after dropping it, each of those calls is a full scan of events. A realm with ~1M metrics rows at the default batch of 1000 would scan events ~1000 times for nothing. WDYT about dropping a purger from the loop once it returns 0 (or fewer than batchSize)?

claimable = claimable.stream().filter(p -> requested.contains(p.realmId())).toList();
claimable.forEach(p -> requested.remove(p.realmId()));
// Naming the realms that hold no queued purge beats reporting nothing drained for them.
requested.forEach(r -> progress.accept("Realm " + r + " has no queued purge."));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: listClaimable leaves out realms whose claim is still live, so --realm r while another node drains r reports "no queued purge". Could we tell these apart, e.g. "Realm r is being drained by ", using claimedBy?

Purging a realm on the relational-JDBC backend leaves its time-series data
behind. deleteAll removes the realm's entities, grant records, principal
authentication data and policy mappings, but nothing deletes its rows in
events, scan_metrics_report and commit_metrics_report. Those rows are keyed by
a realm id that no longer exists, so no later operation reaches them: purging
the realm again does not, and age-based retention cannot, because there is no
realm left to run it for.

Deleting them inline is not the right fix. A realm with a long history can hold
far more time-series rows than metadata rows, and one unbounded delete inside
the purge transaction is the statement that makes a purge time out or hold locks
for minutes.

So the purge of that data becomes asynchronous, queued and batched. deleteAll
enqueues the realm into a new realm_purge marker table as the last statement of
its own transaction, so the marker never commits without the metadata delete and
the metadata delete never commits without the marker. A new operator-run admin
command, purge-drain, processes the queue: it claims each realm under a lease so
only one drainer works a realm at a time across nodes, deletes its rows in
bounded batches, then clears the marker. A realm whose drain fails or is
interrupted keeps its marker and is retried once the lease expires, which makes
the command safe to run repeatedly and concurrently and suitable for scheduling,
the same model as nosql maintenance-run. Per-table purgers are discovered
through CDI, so covering another realm-scoped table means adding a producer
rather than editing the drainer.

realm_purge is a new table, so this requires schema v7 and the migration must be
applied before starting the new binary, as for v6. The migration SQL goes in the
metastore documentation, with a note in the changelog.

A drain is scoped to the realm incarnation that was purged: the marker records
when the purge was requested, and a purger deletes only rows older than that, so
a realm bootstrapped again under the same name before its drain runs keeps its
own data.

Purging a realm name again carries the marker forward to the later purge and
releases any outstanding claim, so data written between two purges is still
reclaimed, and a drain running against the earlier cutoff stands down rather
than clearing a marker that no longer describes the work.

A claim is timed when it is taken and extended before each batch, and the marker
is retired only by the node that still holds it, so a drain that outlives its
lease stops instead of racing the node that took it over, and cannot leave a
realm with unreferenced data and no marker.

No index is added for the drain. events carries only its primary key, so each
batch scans for the realm's rows, which is accepted here rather than charging
every event write for an index that only an occasional purge reads. A store is
dropped from the drain once it returns fewer rows than the batch, since the
cutoff is fixed and it can acquire none later, so the scan is paid once per
store rather than once per round.

The command resolves the drain from the JDBC backend's own CDI bean rather than
through a persistence-SPI method, following how the admin tool reaches NoSQL
maintenance, and reports a usage error on other backends. It accepts realms to
restrict the drain to, and reports a line per realm as it works, naming the rows
deleted and the drainer holding any realm it had to leave, so that an operator
reading its output can tell what a count of drained realms means.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants