|
4 | 4 |
|
5 | 5 | import com.azure.cosmos.CosmosAsyncContainer; |
6 | 6 | import com.azure.cosmos.implementation.PartitionKeyRange; |
7 | | -import com.azure.cosmos.implementation.Resource; |
8 | 7 | import com.azure.cosmos.implementation.changefeed.ChangeFeedContextClient; |
9 | 8 | import com.azure.cosmos.implementation.changefeed.Lease; |
10 | 9 | import com.azure.cosmos.implementation.changefeed.LeaseContainer; |
|
17 | 16 | import reactor.core.publisher.Flux; |
18 | 17 | import reactor.core.publisher.Mono; |
19 | 18 |
|
20 | | -import java.util.HashSet; |
21 | | -import java.util.Set; |
| 19 | +import java.util.ArrayList; |
| 20 | +import java.util.List; |
| 21 | +import java.util.Map; |
| 22 | +import java.util.concurrent.ConcurrentHashMap; |
| 23 | +import java.util.stream.Collectors; |
22 | 24 |
|
23 | 25 | import static com.azure.cosmos.BridgeInternal.extractContainerSelfLink; |
24 | 26 |
|
@@ -55,15 +57,19 @@ public PartitionSynchronizerImpl( |
55 | 57 |
|
56 | 58 | @Override |
57 | 59 | public Mono<Void> createMissingLeases() { |
| 60 | + Map<String, List<String>> leaseTokenMap = new ConcurrentHashMap<>(); |
| 61 | + |
58 | 62 | return this.enumPartitionKeyRanges() |
59 | | - .map(Resource::getId) |
| 63 | + .map(partitionKeyRange -> { |
| 64 | + leaseTokenMap.put(partitionKeyRange.getId(), partitionKeyRange.getParents()); |
| 65 | + return partitionKeyRange.getId(); |
| 66 | + }) |
60 | 67 | .collectList() |
61 | 68 | .flatMap( partitionKeyRangeIds -> { |
62 | 69 | logger.info( |
63 | 70 | "Checking whether leases for any partition is missing - partitions - {}", |
64 | 71 | String.join(", ", partitionKeyRangeIds)); |
65 | | - Set<String> leaseTokens = new HashSet<>(partitionKeyRangeIds); |
66 | | - return this.createLeases(leaseTokens).then(); |
| 72 | + return this.createLeases(leaseTokenMap).then(); |
67 | 73 | }) |
68 | 74 | .onErrorResume( throwable -> { |
69 | 75 | logger.error("Failed to create missing leases.", throwable); |
@@ -143,41 +149,45 @@ private Flux<PartitionKeyRange> enumPartitionKeyRanges() { |
143 | 149 | } |
144 | 150 |
|
145 | 151 | /** |
146 | | - * Creates leases if they do not exist. This might happen on initial start or if some lease was unexpectedly lost. |
| 152 | + * Creates leases if they do not exist for the partition or partition's parent partitions. |
| 153 | + * This might happen on initial start or if some lease was unexpectedly lost. |
147 | 154 | * <p> |
148 | 155 | * Leases are created without the continuation token. It means partitions will be read according to |
149 | 156 | * 'From Beginning' or 'From current time'. |
150 | 157 | * Same applies also to split partitions. We do not search for parent lease and take continuation token since this |
151 | 158 | * might end up of reprocessing all the events since the split. |
152 | 159 | * |
153 | | - * @param leaseTokens a hash set of all the lease tokens. |
| 160 | + * @param leaseTokenMap a map of all the lease tokens and their mapping parent lease tokens. |
154 | 161 | * @return a deferred computation of this call. |
155 | 162 | */ |
156 | | - private Flux<Lease> createLeases(Set<String> leaseTokens) |
| 163 | + private Flux<Lease> createLeases(Map<String, List<String>> leaseTokenMap) |
157 | 164 | { |
158 | | - Set<String> addedLeaseTokens = new HashSet<>(leaseTokens); |
159 | | - |
| 165 | + List<String> leaseTokensToBeAdded = new ArrayList<>(); |
160 | 166 | return this.leaseContainer.getAllLeases() |
| 167 | + .map(lease -> lease.getLeaseToken()) |
| 168 | + .collectList() |
| 169 | + .flatMapMany(existingLeaseTokens -> { |
| 170 | + // only create lease documents if there is no existing lease document matching the partition or its parent partitions |
| 171 | + leaseTokensToBeAdded.addAll( |
| 172 | + leaseTokenMap.entrySet().stream() |
| 173 | + .filter(entry -> !existingLeaseTokens.contains(entry.getKey())) |
| 174 | + .filter(entry -> entry.getValue() == null || |
| 175 | + entry.getValue().isEmpty() || |
| 176 | + entry.getValue().stream().noneMatch(existingLeaseTokens::contains)) |
| 177 | + .map(Map.Entry::getKey) |
| 178 | + .collect(Collectors.toList()) |
| 179 | + ); |
| 180 | + |
| 181 | + logger.info("Missing lease documents for partitions: [{}]", String.join(", ", leaseTokensToBeAdded)); |
| 182 | + return Flux.fromIterable(leaseTokensToBeAdded); |
| 183 | + }) |
| 184 | + .flatMap(leaseTokenToBeAdded -> { |
| 185 | + logger.debug("Adding a new lease document for partition {}", leaseTokenToBeAdded); |
| 186 | + return this.leaseManager.createLeaseIfNotExist(leaseTokenToBeAdded, null); |
| 187 | + }, this.degreeOfParallelism) |
161 | 188 | .map(lease -> { |
162 | | - if (lease != null) { |
163 | | - logger.debug("Found an existing lease document for partition {}", lease.getLeaseToken()); |
164 | | - // Get leases after getting ranges, to make sure that no other hosts checked in continuation for |
165 | | - // split partition after we got leases. |
166 | | - addedLeaseTokens.remove(lease.getLeaseToken()); |
167 | | - } |
168 | | - |
| 189 | + logger.info("Added new lease document for partition {}", lease.getLeaseToken()); |
169 | 190 | return lease; |
170 | | - }) |
171 | | - .thenMany(Flux.fromIterable(addedLeaseTokens) |
172 | | - .flatMap( addedRangeId -> { |
173 | | - logger.debug("Adding a new lease document for partition {}", addedRangeId); |
174 | | - |
175 | | - return this.leaseManager.createLeaseIfNotExist(addedRangeId, null); |
176 | | - }, this.degreeOfParallelism) |
177 | | - .map( lease -> { |
178 | | - logger.info("Added new lease document for partition {}", lease.getLeaseToken()); |
179 | | - return lease; |
180 | | - }) |
181 | | - ); |
| 191 | + }); |
182 | 192 | } |
183 | 193 | } |
0 commit comments