Skip to content

Centralize entity count persistence in BlockLimitsListener#274

Merged
tastybento merged 1 commit into
developfrom
refactor/centralize-entity-count-persistence
Jul 5, 2026
Merged

Centralize entity count persistence in BlockLimitsListener#274
tastybento merged 1 commit into
developfrom
refactor/centralize-entity-count-persistence

Conversation

@tastybento

Copy link
Copy Markdown
Member

Summary

Follow-up to #273, addressing the review findings on that PR.

Centralize the mutate + save pairing (once and for all)

#273 fixed entity-count persistence by requiring every entity-mutation site to pair the count change with a markChanged() call — a two-step pattern repeated at five sites in EntityLimitListener, with nothing enforcing the pairing. A future entity-mutation site that forgot the second call would silently reintroduce the unpersisted-counts bug.

The public markChanged() is replaced by two wrappers on BlockLimitsListener that do both steps in one call, mirroring how block changes are auto-batched inside process():

  • incrementEntity(Island, Environment, EntityType) — creates the island's count record if needed, increments, batches the save
  • decrementEntity(String, Environment, EntityType) — decrements and batches; no-op if the island has no record (a no-op doesn't count toward the batch threshold)

EntityLimitListener's five call sites now use the wrappers, so the mutate/save pairing can no longer be forgotten. The invariant is documented in CLAUDE.md.

Test coverage for the persistence path

#273 shipped without a test asserting that entity changes actually trigger a save. Six new tests cover the wrappers: increment creates the record and counts, decrement lowers the count, no-op decrement doesn't create records or count toward the batch, the 10th entity change triggers saveObjectAsync (CHANGE_LIMIT = 9), 9 changes don't, and mixed increments/decrements batch together. EntityLimitListenerTest now stubs the mocked wrappers to delegate to the real IslandBlockCount so its count assertions keep exercising the real flow.

Cleanup

updateSaveMap()'s saveMap.putIfAbsent(id, 0) was redundant — merge(id, 1, Integer::sum) already creates the entry. Removed.

Testing

All 256 tests pass (mvn test), including the 6 new persistence tests.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LVX8dZq1uKYKS53p1zvpku

Follow-up to #273. That fix required every entity-mutation site to
remember to pair the count change with a markChanged() call — a
pattern repeated five times that a future call site could silently
forget, reintroducing the unpersisted-counts bug.

Replace the public markChanged() with incrementEntity(Island, ...)
and decrementEntity(String, ...) wrappers on BlockLimitsListener
that mutate the count and enroll the change in the batch-save cycle
in one step, mirroring how block changes are auto-batched inside
process(). EntityLimitListener now calls the wrappers, so the
mutate/save pairing can no longer be forgotten.

Also:
- Add tests covering the new persistence path: entity changes
  trigger the batched async save after CHANGE_LIMIT is exceeded,
  no save fires before the threshold, and no-op decrements on
  unknown islands don't count toward the batch.
- Drop the redundant saveMap.putIfAbsent() in updateSaveMap() —
  merge() already creates the entry.
- Document the new invariant in CLAUDE.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVX8dZq1uKYKS53p1zvpku
@sonarqubecloud

sonarqubecloud Bot commented Jul 5, 2026

Copy link
Copy Markdown

@tastybento tastybento merged commit 302c224 into develop Jul 5, 2026
3 checks passed
@tastybento tastybento deleted the refactor/centralize-entity-count-persistence branch July 5, 2026 18:59
@tastybento tastybento mentioned this pull request Jul 5, 2026
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