Skip to content

feat: add spock.resolutions_retention_days GUC and cleanup_resolutions()#412

Merged
mason-sharp merged 4 commits intomainfrom
feature/SPOC-266/resolutions-table-cleanup
Apr 12, 2026
Merged

feat: add spock.resolutions_retention_days GUC and cleanup_resolutions()#412
mason-sharp merged 4 commits intomainfrom
feature/SPOC-266/resolutions-table-cleanup

Conversation

@rasifr
Copy link
Copy Markdown
Member

@rasifr rasifr commented Apr 9, 2026

Summary

Adds spock.resolutions_retention_days GUC (int, default 100, min 0) to control how long rows are kept in spock.resolutions. The apply worker deletes rows older than the configured window at most once per day. Setting to 0 disables automatic cleanup entirely.

Also adds spock.cleanup_resolutions(), a superuser-only SQL function that returns the number of rows deleted, for manual invocation.

A log_time index is added to both the fresh-install and upgrade scripts to keep the daily DELETE efficient on large tables.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Add automatic and manual retention-based deletion for spock.resolutions via a new GUC spock.resolutions_retention_days (default 100) and a C-backed spock.cleanup_resolutions() function; include apply-worker daily scheduling, index, docs, SQL migration, public declaration, and regression tests.

Changes

Cohort / File(s) Summary
GUC & public declarations
src/spock.c, include/spock_conflict.h
Add spock.resolutions_retention_days GUC (int, default 100, PGC_SIGHUP), introduce extern int spock_resolutions_retention_days; and declare extern uint64 spock_cleanup_resolutions(void);.
Cleanup core & SQL wrapper
src/spock_conflict.c, sql/.../spock--*.sql
Implement spock_cleanup_resolutions_core(int days), worker entry spock_cleanup_resolutions(), and SQL wrapper spock_cleanup_resolutions_sql() (superuser-only). Add SQL function spock.cleanup_resolutions(days integer DEFAULT NULL) RETURNS bigint and revoke PUBLIC execute.
Apply worker integration
src/spock_apply.c
Add daily cleanup interval (RESOLUTIONS_CLEANUP_INTERVAL_MS), track last_cleanup_timestamp, and call spock_cleanup_resolutions() when due and not in a transaction.
Index & migrations
sql/spock--5.0.6--6.0.0-devel.sql, sql/spock--6.0.0-devel.sql
Create non-unique B-tree index on spock.resolutions(log_time) to speed age-based deletions; add migration entries for the cleanup function and privilege revocation.
Documentation
docs/configuring.md, docs/spock_functions/functions/spock_cleanup_resolutions.md
Document spock.resolutions_retention_days (default 100, 0 disables auto cleanup), behavior, and spock.cleanup_resolutions() (args, examples, superuser requirement, relation to spock.save_resolutions).
Regression test & runner
tests/regress/sql/resolutions_retention.sql, Makefile
Add resolutions_retention.sql test exercising retention, manual override, and spock.save_resolutions interaction; update REGRESS list to include/reorder the new test.

Poem

🐇 I nibble logs both old and wan,
I hop at dusk and sweep at dawn.
Days to keep, or days to free,
I tuck the stale rows quietly.
Hop, tidy, hum — a cleaner spree! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly and concisely summarizes the main changes: adding a GUC parameter and a SQL function for managing resolution table retention.
Description check ✅ Passed The description is directly related to the changeset, providing clear details about the GUC configuration, automatic cleanup behavior, manual function usage, and index additions.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/SPOC-266/resolutions-table-cleanup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codacy-production
Copy link
Copy Markdown

codacy-production bot commented Apr 9, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 duplication

Metric Results
Duplication 0

View in Codacy

TIP This summary will be updated as you push new changes. Give us feedback

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/regress/sql/resolutions_retention.sql (1)

94-106: Consider resetting the provider-side retention GUC for completeness.

The cleanup section resets spock.resolutions_retention_days on the subscriber but not on the provider. While the provider doesn't modify this GUC in the test, explicitly resetting both nodes would ensure a clean slate for subsequent tests.

Proposed addition
 \c :subscriber_dsn
 RESET spock.resolutions_retention_days;
 ALTER SYSTEM SET spock.save_resolutions = off;
 SELECT pg_reload_conf();
+
+\c :provider_dsn
+RESET spock.resolutions_retention_days;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/regress/sql/resolutions_retention.sql` around lines 94 - 106, Add
resetting of the provider-side GUC spock.resolutions_retention_days in the
cleanup block so both nodes return to defaults: after switching to the provider
connection (currently via "\c :provider_dsn") ensure you RESET
spock.resolutions_retention_days and, if you disable resolution saving on the
provider, also reapply ALTER SYSTEM SET spock.save_resolutions = off followed by
SELECT pg_reload_conf() to mirror the subscriber cleanup; update the cleanup
sequence around spock.repset_remove_table / spock.replicate_ddl and the
subsequent ALTER SYSTEM/pg_reload_conf calls to include these provider-side
resets.
src/spock_conflict.c (1)

936-1001: Well-designed transaction management for background execution.

The error handling correctly:

  • Saves the caller's memory context before transaction start
  • Copies error data to the surviving context before abort
  • Uses AbortCurrentTransaction() which handles SPI/snapshot cleanup automatically
  • Downgrades to WARNING to avoid disrupting replication

One minor defensive consideration: edata->message could theoretically be NULL in unusual error scenarios.

Defensive NULL check
 		ereport(WARNING,
 				(errcode(edata->sqlerrcode),
-				 errmsg("%s", edata->message)));
+				 errmsg("%s", edata->message ? edata->message : "cleanup_resolutions failed")));
 		FreeErrorData(edata);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/spock_conflict.c` around lines 936 - 1001, The code in
spock_cleanup_resolutions uses edata->message in the ereport(WARNING ...) call
but edata->message can be NULL; update the PG_CATCH block to defensively check
edata->message (the ErrorData pointer returned by CopyErrorData()) and pass a
safe fallback string (e.g. "no error message" or similar) to ereport so you
never call errmsg with a NULL pointer; reference the PG_CATCH block, the edata
variable, CopyErrorData(), and the ereport(WARNING...) invocation when making
this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/spock_apply.c`:
- Around line 3057-3075: The spock_cleanup_resolutions() call runs unprotected
inside the apply loop and can throw into the replication-exception path; wrap
the call in a local exception block using PG_TRY / PG_CATCH around
spock_cleanup_resolutions() (located near last_cleanup_timestamp and
GetCurrentTimestamp checks) so cleanup failures are handled locally: on error,
log the failure (with error details) and clear the error with FlushErrorState()
/ ReThrowError? (do not re-raise), then continue the main loop and still update
last_cleanup_timestamp only on success (or decide to back off by leaving it
unchanged). Ensure no transaction state is assumed (check IsTransactionState
usage remains) and avoid propagating the exception to replication exception
handling.

---

Nitpick comments:
In `@src/spock_conflict.c`:
- Around line 936-1001: The code in spock_cleanup_resolutions uses
edata->message in the ereport(WARNING ...) call but edata->message can be NULL;
update the PG_CATCH block to defensively check edata->message (the ErrorData
pointer returned by CopyErrorData()) and pass a safe fallback string (e.g. "no
error message" or similar) to ereport so you never call errmsg with a NULL
pointer; reference the PG_CATCH block, the edata variable, CopyErrorData(), and
the ereport(WARNING...) invocation when making this change.

In `@tests/regress/sql/resolutions_retention.sql`:
- Around line 94-106: Add resetting of the provider-side GUC
spock.resolutions_retention_days in the cleanup block so both nodes return to
defaults: after switching to the provider connection (currently via "\c
:provider_dsn") ensure you RESET spock.resolutions_retention_days and, if you
disable resolution saving on the provider, also reapply ALTER SYSTEM SET
spock.save_resolutions = off followed by SELECT pg_reload_conf() to mirror the
subscriber cleanup; update the cleanup sequence around spock.repset_remove_table
/ spock.replicate_ddl and the subsequent ALTER SYSTEM/pg_reload_conf calls to
include these provider-side resets.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6f8490f1-c398-45d5-b8f8-42ed592f41ce

📥 Commits

Reviewing files that changed from the base of the PR and between 25feecc and e83a5ef.

⛔ Files ignored due to path filters (1)
  • tests/regress/expected/resolutions_retention.out is excluded by !**/*.out
📒 Files selected for processing (10)
  • Makefile
  • docs/configuring.md
  • docs/spock_functions/functions/spock_cleanup_resolutions.md
  • include/spock_conflict.h
  • sql/spock--5.0.6--6.0.0-devel.sql
  • sql/spock--6.0.0-devel.sql
  • src/spock.c
  • src/spock_apply.c
  • src/spock_conflict.c
  • tests/regress/sql/resolutions_retention.sql

@rasifr rasifr force-pushed the feature/SPOC-266/resolutions-table-cleanup branch from e83a5ef to 8247abd Compare April 9, 2026 11:33
rasifr and others added 3 commits April 9, 2026 17:26
Introduces spock.resolutions_retention_days (int, default 100, min 0)
to control how long rows are kept in spock.resolutions. Rows older
than the configured window are deleted automatically by the apply
worker at most once per day. Setting the GUC to 0 disables automatic
cleanup entirely.

Also adds spock.cleanup_resolutions(), a superuser-only SQL function
that returns the number of rows deleted, for manual invocation.

A log_time index is added to both the fresh-install and upgrade scripts
to keep the daily DELETE efficient on large resolutions tables.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ns()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rasifr rasifr force-pushed the feature/SPOC-266/resolutions-table-cleanup branch from 8247abd to b9adb1d Compare April 9, 2026 13:58
* own transaction and error handling.
*/
if (!IsTransactionState() &&
spock_resolutions_retention_days > 0)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The docs mention it checks save_resolutions, too.

src/spock.c Outdated
NULL,
&spock_resolutions_retention_days,
100, 0, INT_MAX,
PGC_SUSET,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this use PGC_SIGHUP since it is used in the apply worker?

#define is_skipping_changes() (unlikely(!XLogRecPtrIsInvalid(skip_xact_finish_lsn)))

/* How often the apply worker runs spock_cleanup_resolutions() (milliseconds). */
#define RESOLUTIONS_CLEANUP_INTERVAL_MS (86400L * 1000L)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We could leave this, but ms resolution seems like overkill when seconds would do?

@@ -0,0 +1,41 @@
## NAME

spock.cleanup_resolutions()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

well, since you created this, what might be nice is if takes an optional argument that overrides resolutions_retention_days. For example, maybe they set retention to 0, then periodically manually run something like SELECT spock.cleanup_resolutions(60) when they think it is getting too big.

@rasifr rasifr force-pushed the feature/SPOC-266/resolutions-table-cleanup branch from 6746c80 to 2afc55b Compare April 12, 2026 09:06
- Change resolutions_retention_days from PGC_SUSET to PGC_SIGHUP to
  prevent session-level SET; apply worker picks up changes via reload
- Add optional days arg to cleanup_resolutions(); overrides GUC when set
- Fix docs: remove incorrect save_resolutions claim; clarify precedence
- Update regression test to use ALTER SYSTEM SET + pg_reload_conf()
  instead of session-level SET/RESET

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rasifr rasifr force-pushed the feature/SPOC-266/resolutions-table-cleanup branch from 2afc55b to 482410f Compare April 12, 2026 09:07
@mason-sharp mason-sharp merged commit dfb56c2 into main Apr 12, 2026
10 checks passed
@mason-sharp mason-sharp deleted the feature/SPOC-266/resolutions-table-cleanup branch April 12, 2026 23:53
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.

2 participants