|
37 | 37 | import java.util.concurrent.ScheduledFuture;
|
38 | 38 | import java.util.concurrent.TimeUnit;
|
39 | 39 | import java.util.concurrent.atomic.AtomicReference;
|
| 40 | +import java.util.function.Function; |
40 | 41 | import lombok.Getter;
|
41 | 42 | import lombok.extern.slf4j.Slf4j;
|
42 | 43 | import org.apache.pulsar.broker.PulsarServerException;
|
|
79 | 80 | import org.apache.pulsar.broker.loadbalance.extensions.strategy.LeastResourceUsageWithWeight;
|
80 | 81 | import org.apache.pulsar.broker.loadbalance.impl.LoadManagerShared;
|
81 | 82 | import org.apache.pulsar.broker.loadbalance.impl.SimpleResourceAllocationPolicies;
|
| 83 | +import org.apache.pulsar.broker.namespace.NamespaceEphemeralData; |
82 | 84 | import org.apache.pulsar.client.admin.PulsarAdminException;
|
83 | 85 | import org.apache.pulsar.common.naming.NamespaceBundle;
|
84 | 86 | import org.apache.pulsar.common.naming.NamespaceBundleSplitAlgorithm;
|
@@ -342,56 +344,101 @@ public CompletableFuture<Optional<BrokerLookupData>> assign(Optional<ServiceUnit
|
342 | 344 |
|
343 | 345 | final String bundle = serviceUnit.toString();
|
344 | 346 |
|
345 |
| - CompletableFuture<Optional<BrokerLookupData>> future = lookupRequests.computeIfAbsent(bundle, k -> { |
| 347 | + return computeIfAbsent(bundle, k -> { |
346 | 348 | final CompletableFuture<Optional<String>> owner;
|
347 | 349 | // Assign the bundle to channel owner if is internal topic, to avoid circular references.
|
348 | 350 | if (topic.isPresent() && isInternalTopic(topic.get().toString())) {
|
349 | 351 | owner = serviceUnitStateChannel.getChannelOwnerAsync();
|
350 | 352 | } else {
|
351 |
| - owner = serviceUnitStateChannel.getOwnerAsync(bundle).thenCompose(broker -> { |
352 |
| - // If the bundle not assign yet, select and publish assign event to channel. |
353 |
| - if (broker.isEmpty()) { |
354 |
| - return this.selectAsync(serviceUnit).thenCompose(brokerOpt -> { |
355 |
| - if (brokerOpt.isPresent()) { |
356 |
| - assignCounter.incrementSuccess(); |
357 |
| - log.info("Selected new owner broker: {} for bundle: {}.", brokerOpt.get(), bundle); |
358 |
| - return serviceUnitStateChannel.publishAssignEventAsync(bundle, brokerOpt.get()) |
359 |
| - .thenApply(Optional::of); |
360 |
| - } else { |
361 |
| - throw new IllegalStateException( |
362 |
| - "Failed to select the new owner broker for bundle: " + bundle); |
363 |
| - } |
364 |
| - }); |
| 353 | + owner = getOwnerAsync(serviceUnit, bundle, false); |
| 354 | + } |
| 355 | + return getBrokerLookupData(owner, bundle); |
| 356 | + }); |
| 357 | + } |
| 358 | + |
| 359 | + private CompletableFuture<Optional<String>> getOwnerAsync( |
| 360 | + ServiceUnitId serviceUnit, String bundle, boolean ownByLocalBrokerIfAbsent) { |
| 361 | + return serviceUnitStateChannel.getOwnerAsync(bundle).thenCompose(broker -> { |
| 362 | + // If the bundle not assign yet, select and publish assign event to channel. |
| 363 | + if (broker.isEmpty()) { |
| 364 | + CompletableFuture<Optional<String>> selectedBroker; |
| 365 | + if (ownByLocalBrokerIfAbsent){ |
| 366 | + String brokerId = this.brokerRegistry.getBrokerId(); |
| 367 | + selectedBroker = CompletableFuture.completedFuture(Optional.of(brokerId)); |
| 368 | + } else { |
| 369 | + selectedBroker = this.selectAsync(serviceUnit); |
| 370 | + } |
| 371 | + return selectedBroker.thenCompose(brokerOpt -> { |
| 372 | + if (brokerOpt.isPresent()) { |
| 373 | + assignCounter.incrementSuccess(); |
| 374 | + log.info("Selected new owner broker: {} for bundle: {}.", brokerOpt.get(), bundle); |
| 375 | + return serviceUnitStateChannel.publishAssignEventAsync(bundle, brokerOpt.get()) |
| 376 | + .thenApply(Optional::of); |
| 377 | + } else { |
| 378 | + throw new IllegalStateException( |
| 379 | + "Failed to select the new owner broker for bundle: " + bundle); |
365 | 380 | }
|
366 |
| - assignCounter.incrementSkip(); |
367 |
| - // Already assigned, return it. |
368 |
| - return CompletableFuture.completedFuture(broker); |
369 | 381 | });
|
370 | 382 | }
|
| 383 | + assignCounter.incrementSkip(); |
| 384 | + // Already assigned, return it. |
| 385 | + return CompletableFuture.completedFuture(broker); |
| 386 | + }); |
| 387 | + } |
371 | 388 |
|
372 |
| - return owner.thenCompose(broker -> { |
373 |
| - if (broker.isEmpty()) { |
374 |
| - String errorMsg = String.format( |
375 |
| - "Failed to get or assign the owner for bundle:%s", bundle); |
376 |
| - log.error(errorMsg); |
377 |
| - throw new IllegalStateException(errorMsg); |
378 |
| - } |
379 |
| - return CompletableFuture.completedFuture(broker.get()); |
380 |
| - }).thenCompose(broker -> this.getBrokerRegistry().lookupAsync(broker).thenCompose(brokerLookupData -> { |
381 |
| - if (brokerLookupData.isEmpty()) { |
382 |
| - String errorMsg = String.format( |
383 |
| - "Failed to look up a broker registry:%s for bundle:%s", broker, bundle); |
384 |
| - log.error(errorMsg); |
385 |
| - throw new IllegalStateException(errorMsg); |
386 |
| - } |
387 |
| - return CompletableFuture.completedFuture(brokerLookupData); |
388 |
| - })); |
| 389 | + private CompletableFuture<Optional<BrokerLookupData>> getBrokerLookupData( |
| 390 | + CompletableFuture<Optional<String>> owner, |
| 391 | + String bundle) { |
| 392 | + return owner.thenCompose(broker -> { |
| 393 | + if (broker.isEmpty()) { |
| 394 | + String errorMsg = String.format( |
| 395 | + "Failed to get or assign the owner for bundle:%s", bundle); |
| 396 | + log.error(errorMsg); |
| 397 | + throw new IllegalStateException(errorMsg); |
| 398 | + } |
| 399 | + return CompletableFuture.completedFuture(broker.get()); |
| 400 | + }).thenCompose(broker -> this.getBrokerRegistry().lookupAsync(broker).thenCompose(brokerLookupData -> { |
| 401 | + if (brokerLookupData.isEmpty()) { |
| 402 | + String errorMsg = String.format( |
| 403 | + "Failed to look up a broker registry:%s for bundle:%s", broker, bundle); |
| 404 | + log.error(errorMsg); |
| 405 | + throw new IllegalStateException(errorMsg); |
| 406 | + } |
| 407 | + return CompletableFuture.completedFuture(brokerLookupData); |
| 408 | + })); |
| 409 | + } |
| 410 | + |
| 411 | + /** |
| 412 | + * Method to get the current owner of the <code>NamespaceBundle</code> |
| 413 | + * or set the local broker as the owner if absent. |
| 414 | + * |
| 415 | + * @param namespaceBundle the <code>NamespaceBundle</code> |
| 416 | + * @return The ephemeral node data showing the current ownership info in <code>ServiceUnitStateChannel</code> |
| 417 | + */ |
| 418 | + public CompletableFuture<NamespaceEphemeralData> tryAcquiringOwnership(NamespaceBundle namespaceBundle) { |
| 419 | + log.info("Try acquiring ownership for bundle: {} - {}.", namespaceBundle, brokerRegistry.getBrokerId()); |
| 420 | + final String bundle = namespaceBundle.toString(); |
| 421 | + return computeIfAbsent(bundle, k -> { |
| 422 | + final CompletableFuture<Optional<String>> owner = |
| 423 | + this.getOwnerAsync(namespaceBundle, bundle, true); |
| 424 | + return getBrokerLookupData(owner, bundle); |
| 425 | + }).thenApply(brokerLookupData -> { |
| 426 | + if (brokerLookupData.isEmpty()) { |
| 427 | + throw new IllegalStateException( |
| 428 | + "Failed to get the broker lookup data for bundle: " + bundle); |
| 429 | + } |
| 430 | + return brokerLookupData.get().toNamespaceEphemeralData(); |
389 | 431 | });
|
| 432 | + } |
| 433 | + |
| 434 | + private CompletableFuture<Optional<BrokerLookupData>> computeIfAbsent( |
| 435 | + String key, Function<String, CompletableFuture<Optional<BrokerLookupData>>> provider) { |
| 436 | + CompletableFuture<Optional<BrokerLookupData>> future = lookupRequests.computeIfAbsent(key, provider); |
390 | 437 | future.whenComplete((r, t) -> {
|
391 | 438 | if (t != null) {
|
392 | 439 | assignCounter.incrementFailure();
|
393 | 440 | }
|
394 |
| - lookupRequests.remove(bundle); |
| 441 | + lookupRequests.remove(key); |
395 | 442 | }
|
396 | 443 | );
|
397 | 444 | return future;
|
|
0 commit comments