18
18
*/
19
19
package org .apache .pulsar .broker .service ;
20
20
21
+ import static java .util .Objects .requireNonNull ;
21
22
import com .google .common .annotations .VisibleForTesting ;
22
23
import com .google .common .collect .Sets ;
23
24
import java .util .HashSet ;
24
25
import java .util .List ;
25
26
import java .util .Map ;
26
27
import java .util .Objects ;
28
+ import java .util .Optional ;
27
29
import java .util .concurrent .CompletableFuture ;
28
30
import java .util .concurrent .ConcurrentHashMap ;
29
31
import java .util .concurrent .CopyOnWriteArrayList ;
54
56
import org .apache .pulsar .common .naming .TopicName ;
55
57
import org .apache .pulsar .common .policies .data .TopicPolicies ;
56
58
import org .apache .pulsar .common .util .FutureUtil ;
59
+ import org .jetbrains .annotations .NotNull ;
57
60
import org .slf4j .Logger ;
58
61
import org .slf4j .LoggerFactory ;
59
62
@@ -78,8 +81,8 @@ public class SystemTopicBasedTopicPoliciesService implements TopicPoliciesServic
78
81
79
82
private final Map <NamespaceName , CompletableFuture <SystemTopicClient .Reader <PulsarEvent >>>
80
83
readerCaches = new ConcurrentHashMap <>();
81
- @ VisibleForTesting
82
- final Map <NamespaceName , Boolean > policyCacheInitMap = new ConcurrentHashMap <>();
84
+
85
+ final Map <NamespaceName , CompletableFuture < Void > > policyCacheInitMap = new ConcurrentHashMap <>();
83
86
84
87
@ VisibleForTesting
85
88
final Map <TopicName , List <TopicPolicyListener <TopicPolicies >>> listeners = new ConcurrentHashMap <>();
@@ -219,12 +222,12 @@ public TopicPolicies getTopicPolicies(TopicName topicName,
219
222
boolean isGlobal ) throws TopicPoliciesCacheNotInitException {
220
223
if (!policyCacheInitMap .containsKey (topicName .getNamespaceObject ())) {
221
224
NamespaceName namespace = topicName .getNamespaceObject ();
222
- prepareInitPoliciesCache (namespace , new CompletableFuture <>() );
225
+ prepareInitPoliciesCacheAsync (namespace );
223
226
}
224
227
225
228
MutablePair <TopicPoliciesCacheNotInitException , TopicPolicies > result = new MutablePair <>();
226
229
policyCacheInitMap .compute (topicName .getNamespaceObject (), (k , initialized ) -> {
227
- if (initialized == null || !initialized ) {
230
+ if (initialized == null || !initialized . isDone () ) {
228
231
result .setLeft (new TopicPoliciesCacheNotInitException ());
229
232
} else {
230
233
TopicPolicies topicPolicies =
@@ -242,6 +245,34 @@ public TopicPolicies getTopicPolicies(TopicName topicName,
242
245
}
243
246
}
244
247
248
+ @ NotNull
249
+ @ Override
250
+ public CompletableFuture <Optional <TopicPolicies >> getTopicPoliciesAsync (@ NotNull TopicName topicName ,
251
+ boolean isGlobal ) {
252
+ requireNonNull (topicName );
253
+ final CompletableFuture <Void > preparedFuture = prepareInitPoliciesCacheAsync (topicName .getNamespaceObject ());
254
+ return preparedFuture .thenApply (__ -> {
255
+ final TopicPolicies candidatePolicies = isGlobal
256
+ ? globalPoliciesCache .get (TopicName .get (topicName .getPartitionedTopicName ()))
257
+ : policiesCache .get (TopicName .get (topicName .getPartitionedTopicName ()));
258
+ return Optional .ofNullable (candidatePolicies );
259
+ });
260
+ }
261
+
262
+ @ NotNull
263
+ @ Override
264
+ public CompletableFuture <Optional <TopicPolicies >> getTopicPoliciesAsync (@ NotNull TopicName topicName ) {
265
+ requireNonNull (topicName );
266
+ final CompletableFuture <Void > preparedFuture = prepareInitPoliciesCacheAsync (topicName .getNamespaceObject ());
267
+ return preparedFuture .thenApply (__ -> {
268
+ final TopicPolicies localPolicies = policiesCache .get (TopicName .get (topicName .getPartitionedTopicName ()));
269
+ if (localPolicies != null ) {
270
+ return Optional .of (localPolicies );
271
+ }
272
+ return Optional .ofNullable (globalPoliciesCache .get (TopicName .get (topicName .getPartitionedTopicName ())));
273
+ });
274
+ }
275
+
245
276
@ Override
246
277
public TopicPolicies getTopicPoliciesIfExists (TopicName topicName ) {
247
278
return policiesCache .get (TopicName .get (topicName .getPartitionedTopicName ()));
@@ -265,39 +296,48 @@ public CompletableFuture<TopicPolicies> getTopicPoliciesBypassCacheAsync(TopicNa
265
296
266
297
@ Override
267
298
public CompletableFuture <Void > addOwnedNamespaceBundleAsync (NamespaceBundle namespaceBundle ) {
268
- CompletableFuture <Void > result = new CompletableFuture <>();
269
299
NamespaceName namespace = namespaceBundle .getNamespaceObject ();
270
300
if (NamespaceService .isHeartbeatNamespace (namespace )) {
271
- result .complete (null );
272
- return result ;
301
+ return CompletableFuture .completedFuture (null );
273
302
}
274
303
synchronized (this ) {
275
304
if (readerCaches .get (namespace ) != null ) {
276
305
ownedBundlesCountPerNamespace .get (namespace ).incrementAndGet ();
277
- result . complete (null );
306
+ return CompletableFuture . completedFuture (null );
278
307
} else {
279
- prepareInitPoliciesCache (namespace , result );
308
+ return prepareInitPoliciesCacheAsync (namespace );
280
309
}
281
310
}
282
- return result ;
283
311
}
284
312
285
- private void prepareInitPoliciesCache (@ Nonnull NamespaceName namespace , CompletableFuture <Void > result ) {
286
- if (policyCacheInitMap .putIfAbsent (namespace , false ) == null ) {
287
- CompletableFuture <SystemTopicClient .Reader <PulsarEvent >> readerCompletableFuture =
313
+ private @ Nonnull CompletableFuture <Void > prepareInitPoliciesCacheAsync (@ Nonnull NamespaceName namespace ) {
314
+ requireNonNull (namespace );
315
+ return policyCacheInitMap .computeIfAbsent (namespace , (k ) -> {
316
+ final CompletableFuture <SystemTopicClient .Reader <PulsarEvent >> readerCompletableFuture =
288
317
createSystemTopicClientWithRetry (namespace );
289
318
readerCaches .put (namespace , readerCompletableFuture );
290
319
ownedBundlesCountPerNamespace .putIfAbsent (namespace , new AtomicInteger (1 ));
291
- readerCompletableFuture .thenAccept (reader -> {
292
- initPolicesCache (reader , result );
293
- result .thenRun (() -> readMorePolicies (reader ));
294
- }).exceptionally (ex -> {
295
- log .error ("[{}] Failed to create reader on __change_events topic" , namespace , ex );
296
- cleanCacheAndCloseReader (namespace , false );
297
- result .completeExceptionally (ex );
320
+ final CompletableFuture <Void > initFuture = readerCompletableFuture
321
+ .thenCompose (reader -> {
322
+ final CompletableFuture <Void > stageFuture = new CompletableFuture <>();
323
+ initPolicesCache (reader , stageFuture );
324
+ return stageFuture
325
+ // Read policies in background
326
+ .thenAccept (__ -> readMorePoliciesAsync (reader ));
327
+ });
328
+ initFuture .exceptionally (ex -> {
329
+ try {
330
+ log .error ("[{}] Failed to create reader on __change_events topic" , namespace , ex );
331
+ cleanCacheAndCloseReader (namespace , false );
332
+ } catch (Throwable cleanupEx ) {
333
+ // Adding this catch to avoid break callback chain
334
+ log .error ("[{}] Failed to cleanup reader on __change_events topic" , namespace , cleanupEx );
335
+ }
298
336
return null ;
299
337
});
300
- }
338
+ // let caller know we've got an exception.
339
+ return initFuture ;
340
+ });
301
341
}
302
342
303
343
protected CompletableFuture <SystemTopicClient .Reader <PulsarEvent >> createSystemTopicClientWithRetry (
@@ -381,8 +421,7 @@ private void initPolicesCache(SystemTopicClient.Reader<PulsarEvent> reader, Comp
381
421
if (log .isDebugEnabled ()) {
382
422
log .debug ("[{}] Reach the end of the system topic." , reader .getSystemTopic ().getTopicName ());
383
423
}
384
- policyCacheInitMap .computeIfPresent (
385
- reader .getSystemTopic ().getTopicName ().getNamespaceObject (), (k , v ) -> true );
424
+
386
425
// replay policy message
387
426
policiesCache .forEach (((topicName , topicPolicies ) -> {
388
427
if (listeners .get (topicName ) != null ) {
@@ -395,6 +434,7 @@ private void initPolicesCache(SystemTopicClient.Reader<PulsarEvent> reader, Comp
395
434
}
396
435
}
397
436
}));
437
+
398
438
future .complete (null );
399
439
}
400
440
});
@@ -420,15 +460,21 @@ private void cleanCacheAndCloseReader(@Nonnull NamespaceName namespace, boolean
420
460
});
421
461
}
422
462
423
- private void readMorePolicies (SystemTopicClient .Reader <PulsarEvent > reader ) {
463
+ /**
464
+ * This is an async method for the background reader to continue syncing new messages.
465
+ *
466
+ * Note: You should not do any blocking call here. because it will affect
467
+ * #{@link SystemTopicBasedTopicPoliciesService#getTopicPoliciesAsync(TopicName)} method to block loading topic.
468
+ */
469
+ private void readMorePoliciesAsync (SystemTopicClient .Reader <PulsarEvent > reader ) {
424
470
reader .readNextAsync ()
425
471
.thenAccept (msg -> {
426
472
refreshTopicPoliciesCache (msg );
427
473
notifyListener (msg );
428
474
})
429
475
.whenComplete ((__ , ex ) -> {
430
476
if (ex == null ) {
431
- readMorePolicies (reader );
477
+ readMorePoliciesAsync (reader );
432
478
} else {
433
479
Throwable cause = FutureUtil .unwrapCompletionException (ex );
434
480
if (cause instanceof PulsarClientException .AlreadyClosedException ) {
@@ -437,7 +483,7 @@ private void readMorePolicies(SystemTopicClient.Reader<PulsarEvent> reader) {
437
483
reader .getSystemTopic ().getTopicName ().getNamespaceObject (), false );
438
484
} else {
439
485
log .warn ("Read more topic polices exception, read again." , ex );
440
- readMorePolicies (reader );
486
+ readMorePoliciesAsync (reader );
441
487
}
442
488
}
443
489
});
@@ -605,7 +651,7 @@ boolean checkReaderIsCached(NamespaceName namespaceName) {
605
651
}
606
652
607
653
@ VisibleForTesting
608
- public Boolean getPoliciesCacheInit (NamespaceName namespaceName ) {
654
+ public CompletableFuture < Void > getPoliciesCacheInit (NamespaceName namespaceName ) {
609
655
return policyCacheInitMap .get (namespaceName );
610
656
}
611
657
0 commit comments