Skip to content

fix: use millisecond precision for dataEntry metadata ETag#24046

Open
stian-sandvold wants to merge 2 commits into
masterfrom
fix/dataset-metadata-etag-millis-precision
Open

fix: use millisecond precision for dataEntry metadata ETag#24046
stian-sandvold wants to merge 2 commits into
masterfrom
fix/dataset-metadata-etag-millis-precision

Conversation

@stian-sandvold
Copy link
Copy Markdown
Contributor

What

The GET /api/dataEntry/metadata ETag is built from the latest lastUpdated timestamp across the relevant metadata types, hashed together with the user UID. That timestamp was formatted with DateUtils.toLongDate, which truncates to whole seconds. This bumps it to DateUtils.toLongDateWithMillis so sub-second changes are reflected in the ETag.

Why

With second-level precision, two distinct metadata states that happen within the same wall-clock second produce the same ETag. Consequences:

  • Correctness: a client that fetched the metadata and then sees it change within the same second can be served a stale 304 Not Modified and miss the update until the next change.
  • CI flakiness: DataSetMetadataTest.dataSetMetadataEtagFunctionalityTest creates a dataset and asserts the ETag changed (line 103). When the create lands in the same second as the preceding read, the ETags collide and the assertion fails. This has been intermittently failing the Run api tests job on master (the same test/assertion across multiple unrelated commits, ~25-30% of runs).

Millisecond precision makes same-instant collisions astronomically unlikely and fixes both issues. The MD5 hash length is unchanged, so the existing 34-character ETag assertions still hold.

Testing

  • mvn -pl dhis-web-api spotless:check passes.
  • The change is a one-line precision bump to the existing ETag input; dataSetMetadataEtagFunctionalityTest continues to cover the behaviour.

🤖 Generated with Claude Code

… collisions

The dataEntry/metadata ETag was hashed from a last-modified timestamp
truncated to whole seconds (DateUtils.toLongDate). When a metadata change
landed in the same wall-clock second as a previous fetch, the ETag was
unchanged, so clients could be served a stale 304 Not Modified.

This also intermittently broke CI: DataSetMetadataTest.
dataSetMetadataEtagFunctionalityTest asserts the ETag changes after a
dataset is created, which collided whenever the create happened in the
same second as the prior read (~25-30% of master api-test runs).

Switch to DateUtils.toLongDateWithMillis so the timestamp keeps
millisecond precision. The MD5 hash length is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@stian-sandvold stian-sandvold requested review from a team and jbee May 31, 2026 07:22
Update the expected hash in testGetEtag to match the millisecond-precision
input, and add testGetEtagDistinguishesSubSecondChanges to lock in that
two timestamps one millisecond apart now produce different ETags.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqubecloud
Copy link
Copy Markdown

@codecov
Copy link
Copy Markdown

codecov Bot commented May 31, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 69.04%. Comparing base (17f8133) to head (9109285).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##             master   #24046    +/-   ##
==========================================
  Coverage     69.04%   69.04%            
- Complexity      709     1437   +728     
==========================================
  Files          3684     3684            
  Lines        141646   141647     +1     
  Branches      16453    16453            
==========================================
+ Hits          97797    97798     +1     
  Misses        36243    36243            
  Partials       7606     7606            
Flag Coverage Δ
integration 49.64% <100.00%> (-0.01%) ⬇️
integration-h2 29.64% <0.00%> (-0.01%) ⬇️
unit 34.99% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
.../java/org/hisp/dhis/webapi/utils/ContextUtils.java 58.24% <100.00%> (+0.46%) ⬆️

... and 2 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 05b1dbc...9109285. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@stian-sandvold stian-sandvold requested a review from netroms May 31, 2026 11:07
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