Skip to content

fix: harden stat flush locking and request validation#18

Merged
mjfelis merged 1 commit into
mainfrom
fix/stats-concurrency-followups
Jun 1, 2026
Merged

fix: harden stat flush locking and request validation#18
mjfelis merged 1 commit into
mainfrom
fix/stats-concurrency-followups

Conversation

@mjfelis

@mjfelis mjfelis commented Jun 1, 2026

Copy link
Copy Markdown
Member

Summary

Follow-up to #16, addressing 4 of the 5 hardening items tracked in #17. Compiles cleanly (./mvnw clean compile); no public REST contract change.

Changes

Request validation ordering (StatServerResource)

/stats/view and /stats/compare now validate from/to (and the other required fields) before calling computeGranularity, which dereferences both. Malformed requests now return 400 Bad Request instead of a 500 (NPE).

Defensive handling of concurrently-removed buckets (StatBuilderService)

Both flush methods now null-check the bucket fetched from the cache (it can be removed by a concurrent flush) and skip gracefully, instead of NPE-ing into a broad catch-all.

Stable locking around the periodic flush (StatBuilderService)

In the non-evict branch, the per-bucket lock is now held across the flush and the cache replacement (put). This closes the window where a concurrent increment could be applied to the pre-merge instance and then discarded when the merged instance overwrote it (lost update).

Consistent locking for on-demand flush (StatBuilderService)

flushStats(statClass, entityId) previously ran the flush under the dateStats monitor only. It now uses the same statLockObj → dateStats lock ordering as the periodic flush, so the two paths are mutually exclusive and consistent. Verified no code path acquires dateStats before a stat lock, so the ordering introduces no deadlock.

Not included

Test plan

  • ./mvnw clean compile passes.
  • Lock-ordering reviewed: increments hold only the per-bucket lock; getStat holds dateStats but never acquires a stat lock inside it; flush now consistently takes statLockObj then dateStats.

Refs #17

- StatServerResource: validate from/to (and required fields) before deriving granularity so malformed /stats/view and /stats/compare requests return 400 instead of a 500 NPE
- flushStats: null-check concurrently-removed buckets and skip gracefully
- periodic flush: hold the per-bucket lock across flush + cache replacement to close a lost-update window across merge
- on-demand flushStats(statClass, entityId): use the same statLockObj -> dateStats lock ordering as the periodic flush (consistent locking, no deadlock)

Refs #17. Persistence-context/transaction item (#1) deferred for a test-backed change.
@mjfelis mjfelis merged commit e131953 into main Jun 1, 2026
4 checks passed
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.

1 participant