|
23 | 23 | import java.util.*;
|
24 | 24 | import java.util.concurrent.CompletableFuture;
|
25 | 25 | import java.util.concurrent.CompletionException;
|
| 26 | +import java.util.concurrent.CountDownLatch; |
| 27 | +import java.util.concurrent.ExecutorService; |
| 28 | +import java.util.concurrent.Executors; |
| 29 | +import java.util.concurrent.ThreadFactory; |
| 30 | +import java.util.concurrent.TimeUnit; |
26 | 31 | import java.util.concurrent.atomic.AtomicInteger;
|
27 | 32 | import java.util.stream.Stream;
|
28 | 33 | import okhttp3.mockwebserver.MockResponse;
|
29 | 34 | import okhttp3.mockwebserver.MockWebServer;
|
30 | 35 | import okhttp3.mockwebserver.RecordedRequest;
|
31 | 36 | import org.apache.commons.io.FileUtils;
|
| 37 | +import org.jetbrains.annotations.NotNull; |
32 | 38 | import org.junit.jupiter.api.BeforeEach;
|
33 | 39 | import org.junit.jupiter.api.Test;
|
34 | 40 | import org.junit.jupiter.params.ParameterizedTest;
|
@@ -557,6 +563,72 @@ public void testAssignmentEventCorrectlyDeduplicated() {
|
557 | 563 | verify(mockAssignmentLogger, times(3)).logAssignment(any(Assignment.class));
|
558 | 564 | }
|
559 | 565 |
|
| 566 | + @Test |
| 567 | + public void testAssignmentEventCorrectlyDeduplicatedFromBackgroundThreads() { |
| 568 | + initClientWithAssignmentCache(new LRUInMemoryAssignmentCache(1024)); |
| 569 | + |
| 570 | + Attributes subjectAttributes = new Attributes(); |
| 571 | + subjectAttributes.put("number", EppoValue.valueOf("123456789")); |
| 572 | + |
| 573 | + int numThreads = 10; |
| 574 | + final CountDownLatch threadStartCountDownLatch = new CountDownLatch(numThreads); |
| 575 | + final CountDownLatch getAssignmentStartCountDownLatch = new CountDownLatch(1); |
| 576 | + final List<Integer> assignments = Collections.synchronizedList(Arrays.asList(new Integer[numThreads])); |
| 577 | + try (ExecutorService pool = Executors.newFixedThreadPool(numThreads, new ThreadFactory() { |
| 578 | + private final AtomicInteger threadIndexAtomicInteger = new AtomicInteger(0); |
| 579 | + @Override |
| 580 | + public Thread newThread(@NotNull Runnable runnable) { |
| 581 | + final int threadIndex = threadIndexAtomicInteger.getAndIncrement(); |
| 582 | + return new Thread(runnable, "testAssignmentEventCorrectlyDeduplicatedFromBackgroundThreads-" + threadIndex); |
| 583 | + } |
| 584 | + })) { |
| 585 | + for (int i = 0; i < numThreads; i += 1) { |
| 586 | + final int threadIndex = i; |
| 587 | + pool.execute( |
| 588 | + () -> { |
| 589 | + threadStartCountDownLatch.countDown(); |
| 590 | + boolean shouldStart; |
| 591 | + try { |
| 592 | + shouldStart = getAssignmentStartCountDownLatch.await(1000, TimeUnit.SECONDS); |
| 593 | + } catch (InterruptedException ignored) { |
| 594 | + shouldStart = false; |
| 595 | + } |
| 596 | + final Integer assignment; |
| 597 | + if (shouldStart) { |
| 598 | + assignment = eppoClient.getIntegerAssignment("numeric-one-of", "alice", subjectAttributes, 0); |
| 599 | + } else { |
| 600 | + assignment = null; |
| 601 | + } |
| 602 | + |
| 603 | + assignments.set(threadIndex, assignment); |
| 604 | + } |
| 605 | + ); |
| 606 | + } |
| 607 | + |
| 608 | + boolean shouldStart; |
| 609 | + try { |
| 610 | + shouldStart = threadStartCountDownLatch.await(2, TimeUnit.SECONDS); |
| 611 | + } catch (InterruptedException ignored) { |
| 612 | + shouldStart = false; |
| 613 | + } |
| 614 | + |
| 615 | + assertTrue(shouldStart, "All worker threads did not start"); |
| 616 | + getAssignmentStartCountDownLatch.countDown(); |
| 617 | + } |
| 618 | + |
| 619 | + final List<Integer> expectedAssignments; |
| 620 | + { |
| 621 | + final Integer[] expectedAssignmentsArray = new Integer[numThreads]; |
| 622 | + // `2` matches the attribute `number` value of "123456789" |
| 623 | + Arrays.fill(expectedAssignmentsArray, 2); |
| 624 | + expectedAssignments = Arrays.asList(expectedAssignmentsArray); |
| 625 | + } |
| 626 | + assertEquals(expectedAssignments, assignments); |
| 627 | + |
| 628 | + // `logAssignment` should be called only once. |
| 629 | + verify(mockAssignmentLogger, times(1)).logAssignment(any(Assignment.class)); |
| 630 | + } |
| 631 | + |
560 | 632 | @Test
|
561 | 633 | public void testAssignmentLogErrorNonFatal() {
|
562 | 634 | initClient();
|
|
0 commit comments