Skip to content

Fix expired records not being dropped on receipt - #14154

Merged
wu-sheng merged 1 commit into
apache:masterfrom
killme2008:fix/record-ttl-unit
Oct 20, 2026
Merged

wu-sheng merged 1 commit into
apache:masterfrom
killme2008:fix/record-ttl-unit

Conversation

@killme2008

@killme2008 killme2008 commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Fix expired records not being dropped on receipt

  • Add a unit test to verify that the fix works.
  • Explain briefly why the bug exists and how to fix it.

RecordStreamProcessor compares the record age in milliseconds with recordDataTTL converted to microseconds. With the default TTL of 3 days, a record is only dropped once it is about 3000 days old.

Convert the TTL to milliseconds, the same as the metrics check in IMetricsDAO.isExpiredCache. Added a unit test for records just inside and just past the TTL.

@wu-sheng wu-sheng added this to the 11.1.0 milestone Oct 19, 2026
@wu-sheng wu-sheng added bug Something isn't working and you are sure it's a bug! backend OAP backend related. labels Oct 19, 2026
@wu-sheng

Copy link
Copy Markdown
Member

Thanks @killme2008. The toMillis change is right, but on its own it didn't change anything in production, so I pushed 3863afb on top.

Why. Every record carries a second-precision time bucket (yyyyMMddHHmmss, built by TimeBucket.getRecordTimeBucket), and the record model is registered with DownSampling.Second in create(). But in() decoded the bucket with DownSampling.Minute, which reads every field one place off: seconds as minutes, minutes as hours, and so on, with yyyyMM as the year. A record from 2026-10-14, bucket 20261014225629, decoded to a timestamp in the year 202611. now - recordTimestamp was then about -6.3e15 ms, which is never above any TTL, in microseconds or milliseconds. So expired records were never dropped, rather than dropped after about 1000 times the TTL, and with only the unit fix they still weren't.

The new test passed because it built the bucket with getMinuteTimeBucket, which matches the minute decode but isn't what any record carries. With getRecordTimeBucket in the test, dropsRecordBeyondTTL failed on a3b8a22.

What changed.

  • RecordStreamProcessor: decode the bucket with DownSampling.Second, the record model's precision, as IMetricsDAO.isExpiredCache decodes metrics with their model's precision. toMillis is kept.
  • RecordStreamProcessorTest: build the bucket with TimeBucket.getRecordTimeBucket(...). Both tests pass.
  • changes.md: the entry now describes both causes.

wu-sheng
wu-sheng previously approved these changes Oct 20, 2026
@wu-sheng wu-sheng added the chore Chores about the project, like code cleaning up, typos, upgrading dependencies, etc. label Oct 20, 2026
@wu-sheng

Copy link
Copy Markdown
Member

@killme2008 I pushed 9811bac to fix the BanyanDB Stages failure.

The failure comes from this fix working. The stages case seeds its cold node with data built by a separate generator OAP (test/e2e-v2/cases/storage/banyandb/data-generate), whose mockers send records stamped 3 days in the past on purpose. With expired records now dropped on receipt, the default 3-day recordDataTTL rejected them, so the cold-stage trace list came back empty.

The commit raises SW_CORE_RECORD_DATA_TTL to 5 on the generator OAP only, matching the BanyanDB TTLs it already sets. The stages case keeps the default TTL and reads the old data from the files mounted on its cold node.

Checked locally on an OAP image built from this PR: the generator holds no mock_a_service trace for the last 48–96 hours with TTL 3, even after 4 minutes of sending, and has one within 15 seconds with TTL 5.

RecordStreamProcessor decoded a record's second-precision time bucket
(yyyyMMddHHmmss) as a minute bucket, which put the record's time far in
the future, and compared the record's age in milliseconds against
recordDataTTL converted to microseconds, so no record was ever treated as
expired. Decode the bucket at second precision and convert the TTL to
milliseconds, as the metrics check in IMetricsDAO.isExpiredCache does.

The BanyanDB stages data generator seeds the cold node with records
stamped 3 days in the past, so its OAP raises recordDataTTL to 5 days,
matching the BanyanDB TTLs it sets; the stages case keeps the default.

Co-authored-by: Wu Sheng <wu.sheng@foxmail.com>
@wu-sheng
wu-sheng force-pushed the fix/record-ttl-unit branch from 9811bac to 12b0978 Compare October 20, 2026 05:45
@killme2008

Copy link
Copy Markdown
Contributor Author

@killme2008 I pushed 9811bac to fix the BanyanDB Stages failure.

The failure comes from this fix working. The stages case seeds its cold node with data built by a separate generator OAP (test/e2e-v2/cases/storage/banyandb/data-generate), whose mockers send records stamped 3 days in the past on purpose. With expired records now dropped on receipt, the default 3-day recordDataTTL rejected them, so the cold-stage trace list came back empty.

The commit raises SW_CORE_RECORD_DATA_TTL to 5 on the generator OAP only, matching the BanyanDB TTLs it already sets. The stages case keeps the default TTL and reads the old data from the files mounted on its cold node.

Checked locally on an OAP image built from this PR: the generator holds no mock_a_service trace for the last 48–96 hours with TTL 3, even after 4 minutes of sending, and has one within 15 seconds with TTL 5.

Thank you

@wu-sheng
wu-sheng merged commit 8c8afdf into apache:master Oct 20, 2026
481 of 483 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend OAP backend related. bug Something isn't working and you are sure it's a bug! chore Chores about the project, like code cleaning up, typos, upgrading dependencies, etc.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants