Skip to content

Make TextFileBasedViolationStore thread-safe under parallel test execution#1656

Open
kelunik wants to merge 3 commits into
TNG:mainfrom
kelunik:fix/thread-safe-violation-store
Open

Make TextFileBasedViolationStore thread-safe under parallel test execution#1656
kelunik wants to merge 3 commits into
TNG:mainfrom
kelunik:fix/thread-safe-violation-store

Conversation

@kelunik

@kelunik kelunik commented Jul 6, 2026

Copy link
Copy Markdown

Multiple FreezingArchRule instances each create a fresh TextFileBasedViolationStore and load stored.rules into their own private snapshot. Concurrent saves caused a lost-update: the last writer overwrote the file with only its own entries.

Fix by sharing one FileSyncedProperties per stored.rules canonical path via a static ConcurrentHashMap, and synchronizing writes with a putIfAbsent method that atomically checks, sets, and flushes to disk under one lock.

Fixes #1654.

@StefanGraeber StefanGraeber left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for contributing 🙂
The code itself looks good, just one minor edge case in the case of a non-existing directory which remains as race condition.

to make the pipeline pass, you must include a Signed-off-by: footer to your commit
https://github.com/TNG/ArchUnit/pull/1656/checks?check_run_id=85373415790

@StefanGraeber

Copy link
Copy Markdown
Contributor

you might have a merge conflict with #1655 which will be submitted soon.
It migrates some tests from Junit4 to Junit5 and thus exchange the Temporary File provider Rule with an Extension.
I think it has not touched the same test as you, but I might have missed it.

kelunik and others added 2 commits July 11, 2026 19:57
…ution

Multiple FreezingArchRule instances each create a fresh TextFileBasedViolationStore
and load stored.rules into their own private snapshot. Concurrent saves caused a
lost-update: the last writer overwrote the file with only its own entries.

Fix by sharing one FileSyncedProperties per stored.rules canonical path via a
static ConcurrentHashMap, and synchronizing writes with a putIfAbsent method that
atomically checks, sets, and flushes to disk under one lock.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Niklas Keller <niklas@chrono24.com>
- Move directory creation into FileSyncedProperties.initializePropertiesFile,
  removing the separate ensureExistence call from initialize()
- Use mkdirs() || isDirectory() to handle the race where a concurrent process
  creates the directory between our mkdirs() call returning false and us checking
- Extract concurrency tests into TextFileBasedViolationStoreConcurrencyTest,
  separate initialize-race and save-race scenarios, and call future.get() to
  surface exceptions that would otherwise be silently swallowed by the executor
- Convert both test classes from JUnit 4 to JUnit 5

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Niklas Keller <niklas@chrono24.com>
@kelunik
kelunik force-pushed the fix/thread-safe-violation-store branch from 7518e4c to 85656a5 Compare July 13, 2026 08:04
@kelunik

kelunik commented Jul 13, 2026

Copy link
Copy Markdown
Author

you might have a merge conflict with #1655 which will be submitted soon.

Thanks for the heads up! It didn't have a conflict, but I rebased anyway and converted the test to JUnit 5.

The code itself looks good, just one minor edge case in the case of a non-existing directory which remains as race condition.

True, I added a separate test for that.

Signed-off-by: Niklas Keller <me@kelunik.com>
@kelunik

kelunik commented Jul 15, 2026

Copy link
Copy Markdown
Author

Oh, these old Java versions, should be fixed.

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.

TextFileBasedViolationStore is not thread-safe under parallel test execution

2 participants