Skip to content

[flags] FFL-1236 Add CountDownLatch to FlagsRepository#2947

Merged
jonathanmos merged 2 commits into
feature/feature-flaggingfrom
jmoskovich/ffl-1236/repository-semaphor
Oct 16, 2025
Merged

[flags] FFL-1236 Add CountDownLatch to FlagsRepository#2947
jonathanmos merged 2 commits into
feature/feature-flaggingfrom
jmoskovich/ffl-1236/repository-semaphor

Conversation

@jonathanmos

Copy link
Copy Markdown
Member

What does this PR do?

Add a countDownLatch to the FlagsRepository to prevent defaultValue being returned due to a race condition of persistence not finishing loading on startup before a flag is requested. Now the user will either get the persistence value or, after a maximum of 100ms, will get the default.

Motivation

This aligns with a change performed on iOS to add a semaphore to the repository there.

Additional Notes

Anything else we should know when reviewing?

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)

@jonathanmos
jonathanmos force-pushed the jmoskovich/ffl-1236/repository-semaphor branch from 083c157 to a40e2f8 Compare October 16, 2025 13:36
@jonathanmos
jonathanmos marked this pull request as ready for review October 16, 2025 14:21
@jonathanmos
jonathanmos requested review from a team as code owners October 16, 2025 14:21
@aleksandr-gringauz

aleksandr-gringauz commented Oct 16, 2025

Copy link
Copy Markdown
Contributor

LGTM

If FlagsClient needs to be a blocking API that waits for the persisted flag values, there is no other way indeed. The only slight concern I have is 100ms limit. If 100ms isn't enough, then we have the same problem again. But I guess it will be pretty rare.

Maybe we could simply not specify 100ms at all. The only downside I see is the potential risk of a deadlock in case we made some kind of mistake in our SDK.

Another solution is to have some sort of asynchronous api FlagsClient.resolveFlag(callback: (T) -> Unit). Or some sort of FlagsClientLoadedListener that fires when we read the persisted data from disk, so that the users can know that now they have access to the most recent values.

@typotter typotter 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.

LGTM. Just a question about interrupted threads

atomicState.compareAndSet(null, loadedState)
}
} finally {
persistenceLoadedLatch.countDown()

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.

👍

try {
persistenceLoadedLatch.await(persistenceLoadTimeoutMs, TimeUnit.MILLISECONDS)
} catch (e: InterruptedException) {
Thread.currentThread().interrupt()

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.

InterruptedException is thrown if the countdown latch does not open within the timeout. What is the effect of interrupting the current thread here instead of log and return?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

log and return would swallow the interrupt, but calling Thread.currentThread().interrupt() preserves it for callers further up the stack

@0xnm

0xnm commented Oct 16, 2025

Copy link
Copy Markdown
Member

Another solution is to have some sort of asynchronous api

I think this will be the best approach we can think of for later, because using CountDownLatch with await seems like a hacky solution we need to introduce due to the current design.

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 47.05882% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.70%. Comparing base (a70a89e) to head (53bc3d5).
⚠️ Report is 4 commits behind head on feature/feature-flagging.

Files with missing lines Patch % Lines
...lags/internal/repository/DefaultFlagsRepository.kt 47.06% 8 Missing and 1 partial ⚠️
Additional details and impacted files
@@                     Coverage Diff                      @@
##           feature/feature-flagging    #2947      +/-   ##
============================================================
- Coverage                     70.76%   70.70%   -0.07%     
============================================================
  Files                           835      835              
  Lines                         30388    30400      +12     
  Branches                       5132     5132              
============================================================
- Hits                          21504    21492      -12     
- Misses                         7431     7448      +17     
- Partials                       1453     1460       +7     
Files with missing lines Coverage Δ
...lags/internal/repository/DefaultFlagsRepository.kt 65.31% <47.06%> (+16.66%) ⬆️

... and 37 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jonathanmos
jonathanmos merged commit faed55e into feature/feature-flagging Oct 16, 2025
25 checks passed
@jonathanmos
jonathanmos deleted the jmoskovich/ffl-1236/repository-semaphor branch October 16, 2025 17:15
This was referenced Nov 4, 2025
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.

5 participants