use of com.couchbase.client.core.Core in project couchbase-jvm-clients by couchbase.
the class DefaultConfigurationProviderTest method handlesMultipleBucketOpenInProgress.
/**
* Regression test for JVMCBC-880.
* <p>
* Verifies that when multiple bucket open attempts happen in parallel, the bucketConfigLoadInProgress method
* is not returning false prematurely (namely when only one is finished but one is still oustanding).
*/
@Test
void handlesMultipleBucketOpenInProgress() throws Exception {
Core core = mock(Core.class);
CoreContext ctx = new CoreContext(core, 1, ENVIRONMENT, mock(Authenticator.class));
when(core.context()).thenReturn(ctx);
Set<SeedNode> seedNodes = new HashSet<>(Collections.singletonList(SeedNode.create("127.0.0.1")));
Sinks.One<ProposedBucketConfigContext> bucket1Barrier = Sinks.one();
Sinks.One<ProposedBucketConfigContext> bucket2Barrier = Sinks.one();
ConfigurationProvider cp = new DefaultConfigurationProvider(core, seedNodes) {
@Override
protected Mono<ProposedBucketConfigContext> loadBucketConfigForSeed(NodeIdentifier identifier, int mappedKvPort, int mappedManagerPort, String name, Optional<String> alternateAddress) {
if (name.equals("bucket1")) {
return bucket1Barrier.asMono();
} else {
return bucket2Barrier.asMono();
}
}
@Override
public void proposeBucketConfig(ProposedBucketConfigContext ctx) {
}
@Override
protected Mono<Void> registerRefresher(String bucket) {
return Mono.empty();
}
};
assertFalse(cp.bucketConfigLoadInProgress());
CountDownLatch latch = new CountDownLatch(2);
cp.openBucket("bucket1").subscribe(unused -> {
}, Assertions::fail, () -> {
assertTrue(cp.bucketConfigLoadInProgress());
latch.countDown();
});
cp.openBucket("bucket2").subscribe(unused -> {
}, Assertions::fail, () -> {
assertFalse(cp.bucketConfigLoadInProgress());
latch.countDown();
});
// we pretend bucket 1 takes 1ms, while bucket2 takes 200ms
Mono.delay(Duration.ofMillis(1)).subscribe(i -> bucket1Barrier.tryEmitValue(new ProposedBucketConfigContext("bucket1", "{}", "127.0.0.1")));
Mono.delay(Duration.ofMillis(200)).subscribe(i -> bucket2Barrier.tryEmitValue(new ProposedBucketConfigContext("bucket2", "{}", "127.0.0.1")));
assertTrue(latch.await(5, TimeUnit.SECONDS));
}
use of com.couchbase.client.core.Core in project couchbase-jvm-clients by couchbase.
the class DefaultConfigurationProviderTest method ignoresMultipleCollectionIdRefreshAttempts.
/**
* It is allowed to have multiple attempts in-flight at the same time, but not for the same collection identifier
* (since this would just spam the cluster unnecessarily).
*/
@Test
void ignoresMultipleCollectionIdRefreshAttempts() {
Core core = mock(Core.class);
CoreContext ctx = new CoreContext(core, 1, ENVIRONMENT, mock(Authenticator.class));
when(core.context()).thenReturn(ctx);
Set<SeedNode> seedNodes = new HashSet<>(Collections.singletonList(SeedNode.create("127.0.0.1")));
List<GetCollectionIdRequest> capturedRequests = new ArrayList<>();
doAnswer(invocation -> {
capturedRequests.add(invocation.getArgument(0));
return null;
}).when(core).send(any(GetCollectionIdRequest.class));
DefaultConfigurationProvider provider = new DefaultConfigurationProvider(core, seedNodes);
assertFalse(provider.collectionRefreshInProgress());
CollectionIdentifier identifier1 = new CollectionIdentifier("bucket", Optional.of("scope"), Optional.of("collection"));
CollectionIdentifier identifier2 = new CollectionIdentifier("bucket", Optional.of("_default"), Optional.of("_default"));
provider.refreshCollectionId(identifier1);
assertEquals(1, provider.collectionMapRefreshInProgress.size());
assertTrue(provider.collectionMapRefreshInProgress.contains(identifier1));
provider.refreshCollectionId(identifier2);
assertEquals(2, provider.collectionMapRefreshInProgress.size());
assertTrue(provider.collectionMapRefreshInProgress.contains(identifier2));
provider.refreshCollectionId(identifier2);
assertEquals(2, provider.collectionMapRefreshInProgress.size());
assertTrue(provider.collectionMapRefreshInProgress.contains(identifier2));
boolean found = false;
for (Event event : EVENT_BUS.publishedEvents()) {
if (event instanceof CollectionMapRefreshIgnoredEvent) {
assertEquals(((CollectionMapRefreshIgnoredEvent) event).collectionIdentifier(), identifier2);
found = true;
}
}
assertTrue(found);
capturedRequests.get(0).succeed(new GetCollectionIdResponse(ResponseStatus.SUCCESS, Optional.of(1234L)));
assertTrue(provider.collectionRefreshInProgress());
capturedRequests.get(1).cancel(CancellationReason.TIMEOUT);
waitUntilCondition(() -> !provider.collectionRefreshInProgress());
found = false;
for (Event event : EVENT_BUS.publishedEvents()) {
if (event instanceof CollectionMapRefreshFailedEvent) {
assertEquals(((CollectionMapRefreshFailedEvent) event).collectionIdentifier(), identifier2);
found = true;
}
}
assertTrue(found);
}
use of com.couchbase.client.core.Core in project couchbase-jvm-clients by couchbase.
the class ReplicaHelper method getAllReplicasRequests.
/**
* Helper method to assemble a stream of requests to the active and all replicas
*
* @param core the core to execute the request
* @param collectionIdentifier the collection containing the document
* @param documentId the ID of the document
* @param clientContext (nullable) client context info
* @param retryStrategy the retry strategy to use
* @param timeout the timeout until we need to stop the get all replicas
* @param parent the "get all/any replicas" request span
* @return a stream of requests.
*/
public static CompletableFuture<Stream<GetRequest>> getAllReplicasRequests(final Core core, final CollectionIdentifier collectionIdentifier, final String documentId, final Map<String, Object> clientContext, final RetryStrategy retryStrategy, final Duration timeout, final RequestSpan parent) {
notNullOrEmpty(documentId, "Id");
final CoreContext coreContext = core.context();
final CoreEnvironment environment = coreContext.environment();
final BucketConfig config = core.clusterConfig().bucketConfig(collectionIdentifier.bucket());
if (config instanceof CouchbaseBucketConfig) {
int numReplicas = ((CouchbaseBucketConfig) config).numberOfReplicas();
List<GetRequest> requests = new ArrayList<>(numReplicas + 1);
RequestSpan span = environment.requestTracer().requestSpan(TracingIdentifiers.SPAN_REQUEST_KV_GET, parent);
GetRequest activeRequest = new GetRequest(documentId, timeout, coreContext, collectionIdentifier, retryStrategy, span);
activeRequest.context().clientContext(clientContext);
requests.add(activeRequest);
for (short replica = 1; replica <= numReplicas; replica++) {
RequestSpan replicaSpan = environment.requestTracer().requestSpan(TracingIdentifiers.SPAN_REQUEST_KV_GET_REPLICA, parent);
ReplicaGetRequest replicaRequest = new ReplicaGetRequest(documentId, timeout, coreContext, collectionIdentifier, retryStrategy, replica, replicaSpan);
replicaRequest.context().clientContext(clientContext);
requests.add(replicaRequest);
}
return CompletableFuture.completedFuture(requests.stream());
} else if (config == null) {
// no bucket config found, it might be in-flight being opened so we need to reschedule the operation until
// the timeout fires!
final Duration retryDelay = Duration.ofMillis(100);
final CompletableFuture<Stream<GetRequest>> future = new CompletableFuture<>();
coreContext.environment().timer().schedule(() -> {
getAllReplicasRequests(core, collectionIdentifier, documentId, clientContext, retryStrategy, timeout.minus(retryDelay), parent).whenComplete((getRequestStream, throwable) -> {
if (throwable != null) {
future.completeExceptionally(throwable);
} else {
future.complete(getRequestStream);
}
});
}, retryDelay);
return future;
} else {
final CompletableFuture<Stream<GetRequest>> future = new CompletableFuture<>();
future.completeExceptionally(CommonExceptions.getFromReplicaNotCouchbaseBucket());
return future;
}
}
use of com.couchbase.client.core.Core in project couchbase-jvm-clients by couchbase.
the class ReplicaHelper method getAllReplicasReactive.
/**
* @param clientContext (nullable)
* @param parentSpan (nullable)
*/
public static Flux<GetReplicaResponse> getAllReplicasReactive(final Core core, final CollectionIdentifier collectionIdentifier, final String documentId, final Duration timeout, final RetryStrategy retryStrategy, Map<String, Object> clientContext, RequestSpan parentSpan) {
notNullOrEmpty(documentId, "Id", () -> ReducedKeyValueErrorContext.create(documentId, collectionIdentifier));
CoreEnvironment env = core.context().environment();
RequestSpan getAllSpan = env.requestTracer().requestSpan(TracingIdentifiers.SPAN_GET_ALL_REPLICAS, parentSpan);
getAllSpan.attribute(TracingIdentifiers.ATTR_SYSTEM, TracingIdentifiers.ATTR_SYSTEM_COUCHBASE);
return Reactor.toMono(() -> getAllReplicasRequests(core, collectionIdentifier, documentId, clientContext, retryStrategy, timeout, getAllSpan)).flux().flatMap(Flux::fromStream).flatMap(request -> Reactor.wrap(request, get(core, request), true).onErrorResume(t -> {
env.eventBus().publish(new IndividualReplicaGetFailedEvent(request.context()));
// Swallow any errors from individual replicas
return Mono.empty();
}).map(response -> new GetReplicaResponse(response, request instanceof ReplicaGetRequest))).doFinally(signalType -> getAllSpan.end());
}
use of com.couchbase.client.core.Core in project couchbase-jvm-clients by couchbase.
the class ReplicaHelper method getAllReplicasAsync.
/**
* Reads from replicas or the active node based on the options and returns the results as a list
* of futures that might complete or fail.
*
* @param clientContext (nullable)
* @param parentSpan (nullable)
* @param responseMapper converts the GetReplicaResponse to the client's native result type
* @return a list of results from the active and the replica.
*/
public static <R> CompletableFuture<List<CompletableFuture<R>>> getAllReplicasAsync(final Core core, final CollectionIdentifier collectionIdentifier, final String documentId, final Duration timeout, final RetryStrategy retryStrategy, final Map<String, Object> clientContext, final RequestSpan parentSpan, final Function<GetReplicaResponse, R> responseMapper) {
CoreEnvironment env = core.context().environment();
RequestSpan getAllSpan = env.requestTracer().requestSpan(TracingIdentifiers.SPAN_GET_ALL_REPLICAS, parentSpan);
getAllSpan.attribute(TracingIdentifiers.ATTR_SYSTEM, TracingIdentifiers.ATTR_SYSTEM_COUCHBASE);
return getAllReplicasRequests(core, collectionIdentifier, documentId, clientContext, retryStrategy, timeout, getAllSpan).thenApply(stream -> stream.map(request -> get(core, request).thenApply(response -> new GetReplicaResponse(response, request instanceof ReplicaGetRequest)).thenApply(responseMapper)).collect(Collectors.toList())).whenComplete((completableFutures, throwable) -> {
final AtomicInteger toComplete = new AtomicInteger(completableFutures.size());
for (CompletableFuture<R> cf : completableFutures) {
cf.whenComplete((a, b) -> {
if (toComplete.decrementAndGet() == 0) {
getAllSpan.end();
}
});
}
});
}
Aggregations