Skip to content

fix(java): guard JobRec reads with the backend monitor - #569

Merged
pratyush618 merged 2 commits into
ByteVeda:masterfrom
stromanni:fix/inmemory-jobrec-race
Jul 27, 2026
Merged

fix(java): guard JobRec reads with the backend monitor#569
pratyush618 merged 2 commits into
ByteVeda:masterfrom
stromanni:fix/inmemory-jobrec-race

Conversation

@stromanni

@stromanni stromanni commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

InMemoryPubSubTest.publishExpiryCancelsDeliveriesThatOutliveIt fails intermittently in CI (seen on the run for #568, which touches no Java source):

InMemoryPubSubTest > publishExpiryCancelsDeliveriesThatOutliveIt() FAILED
    org.opentest4j.AssertionFailedError at InMemoryPubSubTest.java:285

Line 284 asserts the status and passed; line 285 asserts error and failed. So the job was observed cancelled, without its error.

Cause

jobs is a ConcurrentHashMap, so the lookup is safe — but a JobRec's fields are plain mutable state. Every writer already mutates them under the backend monitor (claimNext, cancel, onComplete, onFail, onCancel, …). The readers that snapshot a job did not take it.

claimNext expires a job in three separate writes:

job.status = "cancelled";
job.completedAt = now();
job.error = "expired before execution";

awaitJob polls getJobJson, which was unsynchronized, so it could build its view between the first and third write — status already cancelled, error still null. Exactly the observed failure.

Fix

getJobJson, getResult, listJobsJson and listJobsAfterJson now take the same monitor the writers hold, making the "JobRec fields are guarded by the backend monitor" invariant true on both sides. All four are pure in-memory reads with no outward calls, so this adds no deadlock risk.

Stats helpers are deliberately left alone: count reads a single field per job, so it has no multi-field snapshot to tear — a momentarily stale count is inherent to an unsynchronized aggregate.

Second commit closes three InMemoryQueueBackend instances in InMemoryPubSubTest with try-with-resources, matching the idiom the rest of the file already uses. Those tests start no workers, so the leak was benign, but it was flagged and the fix is free.

Verification

:test-support:test green across five --rerun invocations, :test-support:spotlessCheck clean. Local runs cannot reproduce the original flake — it passed locally before the fix too — so the argument for the fix rests on the memory-model reasoning above, not on a red-to-green local repro.

Summary by CodeRabbit

  • Bug Fixes
    • Improved thread-safety for in-memory job inspection, cancellation, progress updates, job listing (including pagination), and purging completed jobs.
    • Reduced the chance of inconsistent or incomplete job snapshots appearing during concurrent state changes.
  • Tests
    • Updated in-memory pub/sub tests to scope backend lifetime using automatic resource cleanup, improving reliability and preventing lingering state between test runs.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8471d7c1-8ecd-4e58-bd27-8d3bdab5d160

📥 Commits

Reviewing files that changed from the base of the PR and between bcc09a0 and e3a064b.

📒 Files selected for processing (2)
  • sdks/java/test-support/src/main/java/org/byteveda/taskito/test/InMemoryQueueBackend.java
  • sdks/java/test-support/src/test/java/org/byteveda/taskito/test/InMemoryPubSubTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • sdks/java/test-support/src/test/java/org/byteveda/taskito/test/InMemoryPubSubTest.java

📝 Walkthrough

Walkthrough

InMemoryQueueBackend now synchronizes job inspection, mutation, listing, and purge operations on its monitor. Three pub/sub tests now manage backend instances with try-with-resources while preserving their existing operations and assertions.

Changes

In-memory backend updates

Layer / File(s) Summary
Synchronize backend state access
sdks/java/test-support/src/main/java/org/byteveda/taskito/test/InMemoryQueueBackend.java
Job snapshots, result access, cancellation, progress updates, listings, and completed-job purging now use synchronized methods.
Scope backend resources in tests
sdks/java/test-support/src/test/java/org/byteveda/taskito/test/InMemoryPubSubTest.java
Three tests now construct InMemoryQueueBackend with try-with-resources while preserving their existing operations and assertions.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested labels: storage, tests

Suggested reviewers: pratyush618

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: synchronizing JobRec reads with the backend monitor to fix a race.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@pratyush618
pratyush618 force-pushed the fix/inmemory-jobrec-race branch from d6bf428 to bcc09a0 Compare July 27, 2026 18:28

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@sdks/java/test-support/src/main/java/org/byteveda/taskito/test/InMemoryQueueBackend.java`:
- Around line 116-123: Complete the JobRec monitor contract by making
setProgress use the backend monitor while mutating JobRec.progress, and
synchronize listArchivedAfterJson on that same monitor when reading job fields
through jobView. Preserve the existing synchronized access patterns in
getJobJson and related methods so all multi-field JobRec reads and writes share
one lock.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b145ba9b-c2fd-4927-b771-167bb5ba78c2

📥 Commits

Reviewing files that changed from the base of the PR and between 176be0b and bcc09a0.

📒 Files selected for processing (2)
  • sdks/java/test-support/src/main/java/org/byteveda/taskito/test/InMemoryQueueBackend.java
  • sdks/java/test-support/src/test/java/org/byteveda/taskito/test/InMemoryPubSubTest.java

A poller could see claimNext's half-applied expiry: status cancelled, error still null.
@stromanni
stromanni force-pushed the fix/inmemory-jobrec-race branch from bcc09a0 to e3a064b Compare July 27, 2026 18:50
@pratyush618
pratyush618 merged commit 476b845 into ByteVeda:master Jul 27, 2026
20 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.

2 participants