Skip to content

Fix Concurrency Issues and Add Nitrite Integration Tests#2308

Open
jpgough-ms wants to merge 10 commits intofinos:mainfrom
jpgough-ms:concurrency
Open

Fix Concurrency Issues and Add Nitrite Integration Tests#2308
jpgough-ms wants to merge 10 commits intofinos:mainfrom
jpgough-ms:concurrency

Conversation

@jpgough-ms
Copy link
Copy Markdown
Member

Description

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📚 Documentation update
  • 🎨 Code style/formatting changes
  • ♻️ Refactoring (no functional changes)
  • ⚡ Performance improvements
  • ✅ Test additions or updates
  • 🔧 Chore (maintenance, dependencies, CI, etc.)

Affected Components

  • CLI (cli/)
  • Schema (calm/)
  • CALM AI (calm-ai/)
  • CALM Hub (calm-hub/)
  • CALM Hub UI (calm-hub-ui/)
  • CALM Server (calm-server/)
  • CALM Widgets (calm-widgets/)
  • Documentation (docs/)
  • Shared (shared/)
  • VS Code Extension (calm-plugins/vscode/)
  • Dependencies
  • CI/CD

Commit Message Format ✅

Testing

  • I have tested my changes locally
  • I have added/updated unit tests
  • All existing tests pass

Checklist

  • My commits follow the conventional commit format
  • I have updated documentation if necessary
  • I have added tests for my changes (if applicable)
  • My changes follow the project's coding standards

Replace check-then-act patterns with atomic operations to eliminate
TOCTOU race conditions in all CalmHub store implementations.

MongoDB stores:
- Use unique indexes + try-insertOne/catch-DuplicateKey for namespace,
  domain, and schema stores
- Use atomic updateOne with $elemMatch + $exists filters for
  architecture, pattern, flow, standard, interface, and control stores
- Add MongoIndexInitializer to create unique indexes at startup

Nitrite stores:
- Add ReentrantLock (via Lock interface) with lock/try/finally to
  protect check-then-write in all create operations

Also:
- Add NamespaceAlreadyExistsException checked exception
- Update NamespaceStore interface and NamespaceResource
- Update all affected unit tests

Refs: finos#2284
Add integration test suite for Nitrite storage backend covering all
resource types: ADR, architecture, control, decorator, domain, flow,
interface, namespace, pattern, and standard.

Fix concurrency issues in NitriteInterfaceStore, NitritePatternStore,
and NitriteStandardStore with corresponding unit test updates.
…tegration tests

Add locking to 8 Nitrite store create methods to prevent data loss under
concurrent writes. Add concurrency integration tests (20 threads) for all
entity types across both Nitrite and MongoDB backends.

Stores fixed: Pattern, Architecture, Flow, Standard, Interface, Decorator,
Control (requirements + configurations), and ADR.
# Conflicts:
#	calm-hub/src/main/java/org/finos/calm/store/mongo/MongoInterfaceStore.java
#	calm-hub/src/main/java/org/finos/calm/store/nitrite/NitriteInterfaceStore.java
Replace synchronized keyword in NitriteCounterStore with ReentrantLock
and add missing lock protection in NitriteUserAccessStore to ensure
consistent concurrency control across all Nitrite store implementations.
@github-actions github-actions bot added the calm-hub The Calm Hub Product label Apr 5, 2026
…ore conferences folder

Add comprehensive Javadoc comments to all MongoDB and Nitrite store
classes explaining the concurrency control strategies:
- MongoIndexInitializer: startup index creation lifecycle
- Namespace/Domain stores: optimistic insert with DUPLICATE_KEY handling
- Architecture/Pattern/Flow/Standard/Interface stores: upsert+push pattern
- ControlStore: nested arrayFilters for configuration versions
- CounterStore: atomic findOneAndUpdate with $inc
- CoreSchemaStore: idempotent create with silent duplicate handling
- Nitrite stores: ReentrantLock strategy for standalone mode

Restore accidentally deleted conferences/osff-ln-2025 workshop folder.
@jpgough-ms
Copy link
Copy Markdown
Member Author

At this stage I've decided to leave the concurrency tests as part of the integration suite, they only add 16 seconds to the 1:20 build time on my Mac

Metric Before After Change
Total build time 1:20 min 1:36 min +16 seconds
Unit tests 886 tests 886 tests Same
Integration tests 102 tests 210 tests +108 tests
Test Class main concurrency Delta
MongoConcurrencyIntegration N/A 0.407s (10 tests) New
NitriteConcurrencyIntegration N/A 0.304s (10 tests) New
NitriteAdrIntegration N/A 12.41s (8 tests) New
NitriteArchitectureIntegration N/A 0.052s (4 tests) New
NitriteControlIntegration N/A 0.566s (37 tests) New
NitriteDecoratorIntegration N/A 0.075s (6 tests) New
NitriteDomainIntegration N/A 0.023s (3 tests) New
NitriteFlowIntegration N/A 0.047s (5 tests) New
NitriteInterfaceIntegration N/A 0.135s (13 tests) New
NitriteNamespaceIntegration N/A 0.013s (1 test) New
NitritePatternIntegration N/A 0.035s (4 tests) New
NitriteStandardIntegration N/A 0.065s (7 tests) New

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens CALM Hub’s persistence layer against concurrent writes by making MongoDB operations atomic (and backed by unique indexes) and by serializing Nitrite (standalone) writes with JVM-level locks, alongside expanded integration and unit test coverage for concurrency/idempotency behavior.

Changes:

  • Add MongoDB unique-index initialization at startup and update stores to rely on atomic conditional updates / duplicate-key handling for concurrency safety.
  • Add ReentrantLock-based write serialization to Nitrite stores and adjust Nitrite version handling to return dotted semantic versions.
  • Update unit tests and add end-to-end + concurrency integration tests for both MongoDB and Nitrite backends.

Reviewed changes

Copilot reviewed 59 out of 59 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
calm-hub/src/main/java/org/finos/calm/store/mongo/MongoIndexInitializer.java New startup initializer that ensures unique indexes needed for safe concurrent inserts/updates.
calm-hub/src/main/java/org/finos/calm/store/mongo/MongoNamespaceStore.java Translate Mongo duplicate-key errors into NamespaceAlreadyExistsException (optimistic insert).
calm-hub/src/main/java/org/finos/calm/store/mongo/MongoDomainStore.java Translate Mongo duplicate-key errors into DomainAlreadyExistsException (optimistic insert).
calm-hub/src/main/java/org/finos/calm/store/mongo/MongoCoreSchemaStore.java Make schema version creation idempotent by ignoring duplicate-key inserts.
calm-hub/src/main/java/org/finos/calm/store/mongo/MongoArchitectureStore.java Use atomic conditional updates for version creation under concurrency.
calm-hub/src/main/java/org/finos/calm/store/mongo/MongoPatternStore.java Use atomic conditional updates for version creation under concurrency.
calm-hub/src/main/java/org/finos/calm/store/mongo/MongoFlowStore.java Use atomic conditional updates for version creation under concurrency.
calm-hub/src/main/java/org/finos/calm/store/mongo/MongoStandardStore.java Use atomic conditional updates for version creation under concurrency.
calm-hub/src/main/java/org/finos/calm/store/mongo/MongoInterfaceStore.java Use atomic conditional updates for version creation under concurrency.
calm-hub/src/main/java/org/finos/calm/store/mongo/MongoControlStore.java Use atomic conditional updates (incl. array filters) for nested version creation under concurrency.
calm-hub/src/main/java/org/finos/calm/store/mongo/MongoCounterStore.java Add documentation clarifying atomic counter behavior for ID generation.
calm-hub/src/main/java/org/finos/calm/store/nitrite/NitriteCounterStore.java Replace synchronized counter increment with explicit lock.
calm-hub/src/main/java/org/finos/calm/store/nitrite/NitriteNamespaceStore.java Add lock + NamespaceAlreadyExistsException to prevent duplicate namespace creation in Nitrite.
calm-hub/src/main/java/org/finos/calm/store/nitrite/NitriteDomainStore.java Add lock + duplicate prevention for domain creation in Nitrite.
calm-hub/src/main/java/org/finos/calm/store/nitrite/NitriteStandardStore.java Add locks to creation/versioning; normalize returned version strings to dotted format.
calm-hub/src/main/java/org/finos/calm/store/nitrite/NitriteInterfaceStore.java Add locks to creation/versioning; normalize returned version strings to dotted format.
calm-hub/src/main/java/org/finos/calm/store/nitrite/NitritePatternStore.java Add locks; normalize returned version lists to dotted format.
calm-hub/src/main/java/org/finos/calm/store/nitrite/NitriteFlowStore.java Add locks around ID generation and version creation to avoid races.
calm-hub/src/main/java/org/finos/calm/store/nitrite/NitriteArchitectureStore.java Add locks around ID generation and version creation to avoid races.
calm-hub/src/main/java/org/finos/calm/store/nitrite/NitriteDecoratorStore.java Add lock around decorator creation to avoid ID/list races.
calm-hub/src/main/java/org/finos/calm/store/nitrite/NitriteAdrStore.java Add lock around ADR creation to avoid ID/list races.
calm-hub/src/main/java/org/finos/calm/store/nitrite/NitriteUserAccessStore.java Add lock around user-access creation to avoid ID/insert races.
calm-hub/src/main/java/org/finos/calm/store/nitrite/NitriteControlStore.java Add lock-based serialization for control/config/version writes.
calm-hub/src/main/java/org/finos/calm/store/NamespaceStore.java Update contract to throw NamespaceAlreadyExistsException on create.
calm-hub/src/main/java/org/finos/calm/resources/NamespaceResource.java Handle NamespaceAlreadyExistsException and return HTTP 409 without racy pre-check.
calm-hub/src/main/java/org/finos/calm/domain/exception/NamespaceAlreadyExistsException.java New domain exception for duplicate namespace creation.
calm-hub/src/test/java/org/finos/calm/store/mongo/TestMongoNamespaceStoreShould.java Update tests for duplicate-key behavior (but currently contains an unused import).
calm-hub/src/test/java/org/finos/calm/store/mongo/TestMongoDomainStoreShould.java Update tests to simulate duplicate-key behavior via Mongo exceptions.
calm-hub/src/test/java/org/finos/calm/store/mongo/TestMongoCoreSchemaStoreShould.java Update tests for idempotent duplicate-key handling.
calm-hub/src/test/java/org/finos/calm/store/mongo/TestMongo*StoreShould.java Update version-create tests to stub UpdateResult for new atomic update logic.
calm-hub/src/test/java/org/finos/calm/store/nitrite/TestNitrite*StoreShould.java Update tests to expect dotted semantic versions and string-based version payloads.
calm-hub/src/test/java/org/finos/calm/resources/TestNamespaceResourceShould.java Update resource tests to expect exception-driven 409 behavior (no pre-check).
calm-hub/src/integration-test/java/integration/performance/ConcurrencyTestHelper.java New concurrency harness for integration tests.
calm-hub/src/integration-test/java/integration/performance/MongoConcurrencyIntegration.java New MongoDB concurrency integration tests across entities and version creation.
calm-hub/src/integration-test/java/integration/performance/NitriteConcurrencyIntegration.java New Nitrite concurrency integration tests across entities and version creation.
calm-hub/src/integration-test/java/integration/NitriteIntegration.java New Nitrite end-to-end integration tests for multiple resources.
calm-hub/src/integration-test/java/integration/NitriteEndToEndResource.java New Quarkus test resource for isolated Nitrite DB directory per test run.
calm-hub/src/integration-test/java/integration/NitriteIntegrationTestProfile.java New Quarkus test profile for Nitrite integration tests.
calm-hub/src/integration-test/java/integration/MongoSetup.java Simplify Mongo test setup by checking countDocuments() instead of creating collections manually.
calm-hub/src/integration-test/java/integration/MongoSchemaIntegration.java Simplify schema setup by inserting only when collection is empty.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…methods

Three write methods (createRequirementForVersion, createControlConfiguration,
createConfigurationForVersion) fetched domainDoc twice - once via findControl()
and again before update - causing mutations to be lost. Refactored to fetch
domainDoc once, mutate nested documents in place, and update with the same
reference. Added findControlInDomainDoc and findConfigurationInControlDoc
helpers. Also removed unused ErrorCategory import from
TestMongoNamespaceStoreShould.
@jpgough-ms jpgough-ms changed the title Concurrency Fix Concurrency Issues and Add Nitrite Integration Tests Apr 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

calm-hub The Calm Hub Product

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants