Skip to content

feat: thread-safe user attribute update tracking and safe reading/updating of evaluations#116

Merged
cre8ivejp merged 31 commits intomainfrom
feat/improve-read-evaluation-threadsafe
Jan 22, 2026
Merged

feat: thread-safe user attribute update tracking and safe reading/updating of evaluations#116
cre8ivejp merged 31 commits intomainfrom
feat/improve-read-evaluation-threadsafe

Conversation

@duyhungtnn
Copy link
Copy Markdown
Collaborator

@duyhungtnn duyhungtnn commented Dec 31, 2025

This PR introduces thread-safe user attribute update tracking and improves overall concurrency handling in the evaluation system.

Main changes:

  • Fixed a logical race condition related to userAttributesUpdated by introducing a versioned User Attributes State.
    The boolean userAttributesUpdated flag is replaced with a UserAttributesState struct containing a version counter and an update flag. This prevents race conditions where concurrent attribute updates could be lost during in-flight fetch operations. The clearing logic now uses an atomic compare-and-swap based on version matching.

  • Fixed a potential race condition when reading evaluations that could cause a runtime crash.
    This is addressed by implementing a thread-safe InMemoryCache using a concurrent queue with barrier synchronization, ensuring safe reads and writes without blocking the main thread.

Test infrastructure for fix some flaky tests fail

  • Added a deleteAll() method to EvaluationUserDefaultDaoImpl for centralized test cleanup.
  • Ensured evaluation storage access in tests uses the correct dispatch queue to maintain thread safety.

@duyhungtnn duyhungtnn self-assigned this Dec 31, 2025
@duyhungtnn duyhungtnn changed the title feat: add thread safety to EvaluationStorageImpl with NSLock feat: add thread safety to EvaluationStorage Jan 4, 2026
@duyhungtnn duyhungtnn requested a review from Copilot January 4, 2026 09:10
Copy link
Copy Markdown

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 introduces thread safety improvements to the InMemoryCache component and adds comprehensive concurrency tests to verify thread-safe behavior of the evaluation storage system. The changes aim to prevent data races during concurrent read/write operations.

Key changes:

  • Refactored InMemoryCache to use a concurrent DispatchQueue with barrier writes for thread-safe cache operations
  • Added new test suite EvaluationStorageImplConcurrencyTests with concurrency stress tests and read-during-write performance tests
  • Updated existing E2E tests to properly synchronize evaluation storage access using the client's dispatch queue

Reviewed changes

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

File Description
Bucketeer/Sources/Internal/Evaluation/EvaluationMemCacheDao.swift Implements concurrent queue with barrier writes and concurrent reads for thread-safe cache operations
BucketeerTests/EvaluationStorageImplConcurrencyTests.swift Adds comprehensive concurrency tests including stress tests and non-blocking read verification with a custom BlockingRealSQLDao helper
BucketeerTests/E2E/E2EBKTClientForceUpdateTests.swift Wraps evaluation storage access in dispatchQueue.sync blocks to ensure thread-safe test assertions
BucketeerTests/E2E/E2EEventTests.swift Minor formatting/indentation corrections in test assertions

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

Copy link
Copy Markdown

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

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


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

Copy link
Copy Markdown

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

Copilot reviewed 5 out of 6 changed files in this pull request and generated no new comments.


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

@duyhungtnn duyhungtnn changed the title feat: add thread safety to EvaluationStorage feat: add thread safety when reading/writting the evaluation cache Jan 4, 2026
@duyhungtnn duyhungtnn requested a review from Copilot January 7, 2026 10:15
Copy link
Copy Markdown

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

Copilot reviewed 9 out of 10 changed files in this pull request and generated 3 comments.


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

Copy link
Copy Markdown

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

Copilot reviewed 9 out of 10 changed files in this pull request and generated 4 comments.


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

Copy link
Copy Markdown

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

Copilot reviewed 9 out of 10 changed files in this pull request and generated 2 comments.


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

Copy link
Copy Markdown

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

Copilot reviewed 9 out of 10 changed files in this pull request and generated 1 comment.


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

Copy link
Copy Markdown

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

Copilot reviewed 9 out of 10 changed files in this pull request and generated 2 comments.


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

Copy link
Copy Markdown

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

Copilot reviewed 10 out of 11 changed files in this pull request and generated 3 comments.


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

Updated comments in InMemoryCache to clarify concurrency behavior for set and get methods. Enhanced BlockingRealSQLDao in tests to assert expectation wait results, improving error reporting for failed waits. Removed unused helper from test file.
Introduced tests to verify thread safety and concurrent access behavior of InMemoryCache. Also fixed a typo in E2EBKTClientForceUpdateTests and improved a comment in EvaluationStorageImplConcurrencyTests for clarity.
Added detailed comments to clarify the thread-safety and design decisions behind accessing the memory cache directly in the getBy(featureId:) method. The comments explain the trade-off between responsiveness and data consistency, and why strict locking is avoided to prevent UI freezes.
The setUserAttributesUpdated call is now executed within an execute block to ensure proper handling, possibly for thread safety or sequencing.
Refactored updateUserAttributes to call setUserAttributesUpdated directly, removing the execute wrapper for this call.
Replaces the boolean userAttributesUpdated flag with a version number to prevent race conditions where concurrent updates could be lost during evaluation fetches. Updates EvaluationStorage protocol and implementations to track and clear the flag only when the version matches, ensuring no updates are missed. Adds corresponding unit tests to verify correct behavior.
Introduces a test to verify thread safety of setUserAttributesUpdated and related methods in EvaluationStorageImpl by simulating concurrent updates and clears from different queues. Ensures no race conditions occur and the version increments as expected.
Replaces the mock user defaults DAO with the real EvaluationUserDefaultDaoImpl to ensure thread-safe access during concurrent operations. Increases the number of iterations to 10,000 for more robust concurrency testing and clarifies test assertions and comments.
Renamed userAttributesUpdatedLock to setUserAttributesUpdatedLock and ensured all accesses to userAttributesUpdatedVersion are protected by the lock. Refactored setUserAttributesUpdated and clearUserAttributesUpdated to use the new locking mechanism, improving thread safety in tests.
Added comments to clarify that userAttributesUpdatedVersion increments on setUserAttributesUpdated, and expanded documentation on thread safety and access patterns in getBy for EvaluationStorageImpl.
Reorders async operations in EvaluationStorageTests to reduce race conditions and clarifies the expected outcome. Removes the assertion on userAttributesUpdated's final state, focusing instead on the version counter to verify correct behavior.
Replaces separate userAttributesUpdated and userAttributesUpdatedVersion properties with a unified UserAttributesState struct in EvaluationStorage and related implementations. Updates all usages and tests to use the new struct, improving atomicity and clarity of user attribute update tracking.
Updated the documentation for UserAttributesState to clarify its intended use as an in-memory snapshot for a single app session and to specify the persistence behavior of its properties.
Replaces the userAttributesUpdated property with userAttributesState.isUpdated in EvaluationStorage and related tests. This change improves clarity by using the UserAttributesState struct for tracking updates, and updates all references in implementation and tests accordingly.
Added a comment explaining the purpose of the userAttributesUpdatedVersion variable and its thread safety mechanism in EvaluationStorageImpl.
Replaces version-based clearing of the user-attributes-updated flag with a method that accepts a UserAttributesState snapshot, improving atomicity and thread-safety. Updates EvaluationStorage protocol, its implementation, and related tests and mocks to use the new approach.
Introduces a deleteAll() method to EvaluationUserDefaultDaoImpl for removing all related UserDefaults keys, and updates E2E tests to use this method for setup and teardown. Adds removeObject to Defaults protocol and MockDefaults, and makes minor improvements to MockEvaluationStorage and test concurrency handling.
Deleted a detailed comment describing a logical race condition with the userAttributesUpdated boolean and a proposed solution. The comment is no longer needed, possibly due to code changes or refactoring.
Refactored MockDefaults to use a concurrent DispatchQueue for thread-safe access to the underlying dictionary. Read operations are now concurrent, while write operations use barrier flags to ensure exclusive access and data consistency.
Added detailed documentation to UserAttributesState explaining the ephemeral version counter and optimistic locking pattern. Removed unused userAttributesUpdated property and redundant dict property in MockDefaults for code clarity.
Improved documentation in EvaluationStorage and its tests to clarify the behavior of the version counter and update flag, especially regarding race conditions and expected outcomes during concurrent operations.
Introduced EvaluationUserDefaultsKey enum to centralize user defaults keys and updated references throughout the codebase. Added UserDefaults.removeAllEvaluationData() helper for test cleanup. Updated tests and mocks to use new key management and helper method, improving maintainability and reducing duplication.
@duyhungtnn duyhungtnn force-pushed the feat/improve-read-evaluation-threadsafe branch from db1e644 to dcd7cd1 Compare January 19, 2026 14:22
@duyhungtnn duyhungtnn requested a review from Copilot January 19, 2026 14:22
Copy link
Copy Markdown

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

Copilot reviewed 19 out of 19 changed files in this pull request and generated no new comments.


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

Copy link
Copy Markdown
Member

@cre8ivejp cre8ivejp left a comment

Choose a reason for hiding this comment

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

Thank you!

@cre8ivejp cre8ivejp merged commit b88f0a3 into main Jan 22, 2026
15 checks passed
@cre8ivejp cre8ivejp deleted the feat/improve-read-evaluation-threadsafe branch January 22, 2026 02:50
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.

3 participants