Skip to content

feat(core): use wall clock for TTL to prevent accidental data loss#6531

Merged
bluestreak01 merged 5 commits intomasterfrom
vi_wall_ttl
Dec 13, 2025
Merged

feat(core): use wall clock for TTL to prevent accidental data loss#6531
bluestreak01 merged 5 commits intomasterfrom
vi_wall_ttl

Conversation

@bluestreak01
Copy link
Copy Markdown
Member

@bluestreak01 bluestreak01 commented Dec 13, 2025

Summary

Adds wall-clock protection to TTL enforcement to prevent accidental data loss when future timestamps are inserted.

Problem

When a table has TTL enabled and a row with a far-future timestamp is inserted (e.g., year 2100), TTL enforcement would use that future timestamp as the reference point. This causes all existing data to appear "expired" relative to the future timestamp, leading to unexpected partition eviction.

Solution

TTL enforcement now uses min(maxTimestamp, wallClockTime) instead of just maxTimestamp. This ensures that inserting future timestamps cannot cause existing data to be evicted prematurely.

A new configuration option cairo.ttl.use.wall.clock (default: true) allows opting out of this behavior if needed.

Additional Optimization

Reduced microsecond clock syscalls in commit paths:

  • WAL commit: 2-3 syscalls → 1 syscall
  • Clock value is captured once and passed through processWalCommit(), housekeep(), and enforceTtl()

Configuration

Property Default Description
cairo.ttl.use.wall.clock true When true, TTL uses min(maxTimestamp, wallClock) to prevent data loss from future timestamps

Test Plan

  • testFutureTimestampDoesNotWipeTable - verifies wall-clock protection works
  • testFutureTimestampDoesNotWipeTableNanos - same for TIMESTAMP_NS
  • testFutureTimestampWipesTableWhenWallClockDisabled - verifies opt-out behavior

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Dec 13, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Introduces a configurable TTL wall-clock feature to QuestDB. Adds a new PropertyKey CAIRO_TTL_USE_WALL_CLOCK with corresponding configuration and default method, wires wall-clock time propagation through WAL commit pathways in TableWriter, and updates TTL enforcement logic to incorporate wall-clock-aware calculations when enabled.

Changes

Cohort / File(s) Summary
Configuration Property Definition
core/src/main/java/io/questdb/PropertyKey.java, core/src/main/java/io/questdb/cairo/CairoConfiguration.java
Added enum constant CAIRO_TTL_USE_WALL_CLOCK and public default method isTtlWallClockEnabled() returning true, with Javadoc describing wall-clock vs. max-timestamp TTL enforcement behavior.
Configuration Initialization
core/src/main/java/io/questdb/PropServerConfiguration.java
Added private ttlUseWallClock field initialized from CAIRO_TTL_USE_WALL_CLOCK property and implemented isTtlWallClockEnabled() accessor in inner PropCairoConfiguration class.
Default Configuration
core/src/main/resources/io/questdb/site/conf/server.conf
Added cairo.ttl.use.wall.clock property with descriptive comment documenting wall-clock-aware TTL enforcement behavior.
WAL Commit & TTL Logic
core/src/main/java/io/questdb/cairo/TableWriter.java
Extended processWalCommit() and housekeep() method signatures to accept wallClockMicros parameter; introduced wall-clock capture in commitWalInsertTransactions(); refactored TTL enforcement in enforceTTL() to incorporate wall-clock-aware expiration logic when enabled.
Test Infrastructure Updates
core/src/test/java/io/questdb/test/ServerMainTest.java, core/src/test/java/io/questdb/test/cairo/TtlTest.java, core/src/test/java/io/questdb/test/cairo/mv/MatViewTest.java
Updated expected output in ServerMainTest to include new property; reformatted assertSql blocks; added three new test methods (testFutureTimestampDoesNotWipeTable*) to cover TTL behavior with future timestamps and wall-clock control; adjusted test setup in MatViewTest to advance wall clock before inserting future timestamps.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • TableWriter.java requires careful review of wall-clock propagation logic through WAL commit pathways and the refactored TTL enforcement implementation, particularly the interaction between wall-clock time and max-timestamp calculations
  • TTL enforcement changes in enforceTTL() involve nuanced logic modifications that affect data retention behavior; verify correctness of wall-clock vs. max-timestamp comparisons
  • Method signature changes to processWalCommit() and housekeep() propagate through multiple call sites; confirm all invocations properly pass wallClockMicros parameter
  • Test coverage for new feature (TtlTest additions) should be validated to ensure edge cases around future timestamps and wall-clock control are properly exercised

Possibly related PRs

  • questdb/questdb#5752: Modifies TableWriter's housekeeping and TTL/housekeep-related code paths with overlapping call sites and TTL enforcement logic.

Suggested labels

Core, Bug

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.12% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: using wall clock for TTL to prevent data loss from future timestamps.
Description check ✅ Passed The description is well-organized and directly related to the changeset, explaining the problem, solution, optimization, and test coverage.

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.

@bluestreak01
Copy link
Copy Markdown
Member Author

@CodeRabbit read the description and review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Dec 13, 2025

@bluestreak01: I'll review the PR changes now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@bluestreak01 bluestreak01 changed the title feat(core): table TTL to be guarded by wall-clock feat(core): use wall clock for TTL to prevent accidental data loss Dec 13, 2025
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: 0

🧹 Nitpick comments (3)
core/src/main/java/io/questdb/cairo/TableWriter.java (1)

147-150: Redundant static imports after switching to TableUtils.*.
With import static io.questdb.cairo.TableUtils.*; you can likely drop explicit openAppend / openRO static imports to avoid duplication (no functional impact).

core/src/test/java/io/questdb/test/cairo/mv/MatViewTest.java (1)

280-287: Test update matches new TTL semantics; consider using the local timestamp parsing helper for consistency.
This setup correctly advances currentMicros (wall clock) so TTL reference time won’t be driven by “future” inserted timestamps anymore, matching the PR’s min(maxTimestamp, wallClock) behavior. Minor consistency nit: nearby code often uses parseFloorPartialTimestamp(...); using the same helper here would keep parsing consistent and reduce direct dependency on MicrosTimestampDriver.INSTANCE.

core/src/main/java/io/questdb/PropServerConfiguration.java (1)

508-509: Good: config-backed ttlUseWallClock is immutable and centrally owned.
No concerns with adding this final field. Consider grouping TTL-related fields together for discoverability (purely optional).

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3137b86 and 3884d9f.

📒 Files selected for processing (8)
  • core/src/main/java/io/questdb/PropServerConfiguration.java (3 hunks)
  • core/src/main/java/io/questdb/PropertyKey.java (1 hunks)
  • core/src/main/java/io/questdb/cairo/CairoConfiguration.java (1 hunks)
  • core/src/main/java/io/questdb/cairo/TableWriter.java (11 hunks)
  • core/src/main/resources/io/questdb/site/conf/server.conf (1 hunks)
  • core/src/test/java/io/questdb/test/ServerMainTest.java (1 hunks)
  • core/src/test/java/io/questdb/test/cairo/TtlTest.java (12 hunks)
  • core/src/test/java/io/questdb/test/cairo/mv/MatViewTest.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
core/src/main/java/io/questdb/cairo/TableWriter.java (2)
core/src/main/java/io/questdb/cairo/TableUtils.java (1)
  • TableUtils (82-2127)
core/src/main/java/io/questdb/cairo/PartitionBy.java (1)
  • PartitionBy (44-192)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (17)
core/src/main/resources/io/questdb/site/conf/server.conf (1)

280-282: LGTM! Clear documentation and safe default.

The new TTL configuration option is well-documented and follows existing conventions. The default value of true appropriately guards against accidental data loss from future timestamps, while still allowing users to opt into the legacy behavior if needed.

core/src/main/java/io/questdb/cairo/TableWriter.java (6)

1269-1360: Wall-clock captured once and threaded through WAL commit + housekeeping: LGTM; verify “timestamp of record” intent.
Capturing wallClockMicros once (Line 1289) and reusing it for processWalCommit(...), lastWalCommitTimestampMicros, and housekeep(...) achieves the syscall reduction goal. Just confirm you’re OK with lastWalCommitTimestampMicros reflecting the start of commitWalInsertTransactions(...) rather than the end of the commit (could slightly skew the commit-latency heuristic).


2118-2191: Non-WAL commit path now uses a single wall clock for housekeeping: looks correct.
Capturing wallClockMicros once per commit(long o3MaxLag) and passing into housekeep(wallClockMicros) is consistent with the WAL path and keeps TTL enforcement aligned to the same reference time.


4061-4114: TTL eviction guarded by wall clock: good safety fix; double-check clock-backwards behavior is acceptable.
Using maxTimestamp = min(maxTimestamp, timestampDriver.fromMicros(wallClockMicros)) when configuration.isTtlWallClockEnabled() is the right mitigation for far-future inserts. Only caveat: if the wall clock jumps backwards, TTL eviction can stall (probably acceptable given the feature’s semantics, but worth explicitly accepting).


5776-5787: housekeep(long wallClockMicros) signature change is clean and keeps TTL deterministic per-commit.
Threading wallClockMicros into enforceTtl(wallClockMicros) avoids extra clock reads and ensures all housekeeping steps in a commit share the same time base.


7593-7833: WAL commit-latency heuristic now uses passed wall clock: OK; validate units and negative deltas.
needFullCommit now includes wallClockMicros - lastWalCommitTimestampMicros > configuration.getCommitLatency() (Line 7658). Please confirm getCommitLatency() is in microseconds (matching MicrosecondClock) and decide how you want to behave if the delta is negative (clock set backwards).


7835-7905: WAL helper overload updated consistently to pass wall clock through.
The wrapper processWalCommit(..., long wallClockMicros) correctly forwards the value into the core processWalCommit(...) overload.

core/src/test/java/io/questdb/test/ServerMainTest.java (1)

248-813: Expected “show parameters” output updated correctly for new TTL wall-clock property.
The added row for cairo.ttl.use.wall.clock matches the new config key’s default true and keeps the set-based comparison consistent.

core/src/main/java/io/questdb/cairo/CairoConfiguration.java (1)

789-801: Good, backward-compatible config surface for TTL wall-clock guarding.
Adding default boolean isTtlWallClockEnabled() with a true default is the right API shape for rolling this out without forcing all CairoConfiguration implementors to update immediately.

core/src/main/java/io/questdb/PropServerConfiguration.java (2)

1409-1410: Default true + getBoolean() wiring looks correct for safe-by-default behavior.
This should also naturally appear in allPairs (and therefore SHOW PROPERTIES) via the existing getString() tracking.


4322-4326: Accessor correctly exposes the toggle through CairoConfiguration.
This is the right place to surface the property for downstream TTL logic.

core/src/main/java/io/questdb/PropertyKey.java (1)

612-614: New PropertyKey constant is safe to add; no code relies on enum ordinals.

The verification script found no instances of PropertyKey.ordinal() usage or ordinal-based branching logic in the codebase. Adding the CAIRO_TTL_USE_WALL_CLOCK entry poses no risk of breaking existing functionality.

core/src/test/java/io/questdb/test/cairo/TtlTest.java (5)

27-27: LGTM! Import necessary for wall-clock configuration.

The PropertyKey import is correctly added to support the new test that toggles the CAIRO_TTL_USE_WALL_CLOCK configuration.


132-137: LGTM! Text block formatting improves readability.

Converting concatenated assertion strings to Java text blocks is a good readability improvement with no semantic changes.

Also applies to: 206-212, 219-224, 550-556, 564-569, 577-582, 590-596, 604-609, 745-751, 759-764, 772-778, 786-791


227-273: Excellent test coverage for wall-clock TTL protection with TIMESTAMP type.

The test correctly verifies that inserting a far-future timestamp (2100) doesn't cause premature eviction of recent data when wall-clock protection is enabled. The timestamp calculations and partition ceiling logic are accurate, and the test properly exercises both WAL and non-WAL modes.


275-321: LGTM! Proper coverage for TIMESTAMP_NS type.

This test correctly validates wall-clock TTL protection for nanosecond-precision timestamps. The intentional duplication ensures both TIMESTAMP and TIMESTAMP_NS column types are tested.


323-363: LGTM! Correctly tests the opt-out behavior.

The test properly verifies that when wall-clock protection is disabled via CAIRO_TTL_USE_WALL_CLOCK=false, TTL enforcement reverts to using only maxTimestamp, allowing far-future timestamps to cause eviction of older data. The property is correctly restored in the finally block.

@bluestreak01 bluestreak01 added Bug Incorrect or unexpected behavior Core Related to storage, data type, etc. labels Dec 13, 2025
@glasstiger
Copy link
Copy Markdown
Contributor

[PR Coverage check]

😍 pass : 45 / 51 (88.24%)

file detail

path covered line new line coverage
🔵 io/questdb/PropServerConfiguration.java 1 2 50.00%
🔵 io/questdb/cairo/TableWriter.java 41 46 89.13%
🔵 io/questdb/PropertyKey.java 2 2 100.00%
🔵 io/questdb/cairo/CairoConfiguration.java 1 1 100.00%

@jerrinot
Copy link
Copy Markdown
Contributor

fixes #5461

@bluestreak01 bluestreak01 merged commit c2343aa into master Dec 13, 2025
43 checks passed
@bluestreak01 bluestreak01 deleted the vi_wall_ttl branch December 13, 2025 12:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Incorrect or unexpected behavior Core Related to storage, data type, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants