|
6 | 6 | import java.util.concurrent.atomic.AtomicInteger; |
7 | 7 |
|
8 | 8 | /** |
| 9 | + * Assumptions: |
| 10 | + * - The latest assigned key ids are from the latest updated buckets |
| 11 | + * - Key ids from these buckets will always be monotonically increasing (apart from wraparound) as they have not rotated again after last assignment |
| 12 | + * |
9 | 13 | * Intended outcomes of KeyIdGenerator: |
10 | | - * - Key ids are always consecutive, starting from 0 |
| 14 | + * - Key ids are always monotonically increasing, starting from 0 |
11 | 15 | * - When the last allocated key id reaches 16777215, the next key id will wrap around to 0 |
12 | 16 | * - Continuing to increment from the highest key id will result in monotonic incrementation of key ids for all newly rotated buckets |
13 | | - * |
14 | | - * Assumptions: |
15 | | - * - The latest assigned key ids are from the latest updated buckets |
16 | | - * - Key ids from these buckets will always be consecutive (apart from wraparound) as they have not rotated again after last assignment |
17 | | - */ |
| 17 | + **/ |
18 | 18 | public class KeyIdGenerator { |
19 | 19 | private static final int MAX_KEY_ID = 16777215; // 3 bytes |
20 | | - private final AtomicInteger lastActiveKeyId; |
| 20 | + private final AtomicInteger nextActiveKeyId; |
21 | 21 |
|
22 | 22 | public KeyIdGenerator(SaltEntry[] buckets) { |
23 | | - this.lastActiveKeyId = new AtomicInteger(getLastActiveKeyId(buckets)); |
| 23 | + this.nextActiveKeyId = new AtomicInteger(getNextActiveKeyId(buckets)); |
24 | 24 | } |
25 | 25 |
|
26 | | - private static int getLastActiveKeyId(SaltEntry[] buckets) { |
27 | | - long maxLastUpdated = Arrays.stream(buckets).mapToLong(SaltEntry::lastUpdated).max().orElse(0); |
| 26 | + private static int getNextActiveKeyId(SaltEntry[] buckets) { |
| 27 | + long lastUpdatedTimestampWithKey = Arrays.stream(buckets).filter(s -> s.currentKey() != null).mapToLong(SaltEntry::lastUpdated).max().orElse(0); |
| 28 | + if (lastUpdatedTimestampWithKey == 0) return 0; |
| 29 | + |
28 | 30 | int[] lastActiveKeyIdsSorted = Arrays.stream(buckets) |
29 | | - .filter(s -> s.lastUpdated() == maxLastUpdated && s.currentKey() != null) |
| 31 | + .filter(s -> s.lastUpdated() == lastUpdatedTimestampWithKey && s.currentKey() != null) |
30 | 32 | .mapToInt(s -> s.currentKey().id()) |
31 | 33 | .sorted() |
32 | 34 | .toArray(); |
33 | 35 |
|
34 | | - if (lastActiveKeyIdsSorted.length == 0) return MAX_KEY_ID; // so that next ID will start at 0 |
35 | | - |
36 | 36 | int highestId = lastActiveKeyIdsSorted[lastActiveKeyIdsSorted.length - 1]; |
37 | 37 |
|
38 | | - if (highestId < MAX_KEY_ID) return highestId; |
| 38 | + int nextKeyId = highestId + 1; |
| 39 | + if (nextKeyId <= MAX_KEY_ID) return nextKeyId; |
39 | 40 |
|
40 | 41 | // Wrapped case - find the last consecutive ID from 0 |
41 | 42 | for (int i = 0; i < lastActiveKeyIdsSorted.length - 1; i++) { |
42 | 43 | if (lastActiveKeyIdsSorted[i + 1] - lastActiveKeyIdsSorted[i] > 1) { |
43 | | - return lastActiveKeyIdsSorted[i]; |
| 44 | + return lastActiveKeyIdsSorted[i] + 1; |
44 | 45 | } |
45 | 46 | } |
46 | 47 |
|
47 | | - return highestId; |
| 48 | + return 0; |
48 | 49 | } |
49 | 50 |
|
50 | 51 | public int getNextKeyId() { |
51 | | - return lastActiveKeyId.updateAndGet(id -> |
| 52 | + return nextActiveKeyId.getAndUpdate(id -> |
52 | 53 | id + 1 > MAX_KEY_ID ? 0 : id + 1 |
53 | 54 | ); |
54 | 55 | } |
|
0 commit comments