use of com.couchbase.client.core.config.BucketConfig in project couchbase-jvm-clients by couchbase.
the class Core method reconfigureBuckets.
/**
* Contains logic to perform reconfiguration for a bucket config.
*
* @param bucketConfigs the flux of bucket configs currently open.
* @return a mono once reconfiguration for all buckets is complete
*/
private Mono<Void> reconfigureBuckets(final Flux<BucketConfig> bucketConfigs) {
return bucketConfigs.flatMap(bc -> Flux.fromIterable(bc.nodes()).flatMap(ni -> {
boolean tls = coreContext.environment().securityConfig().tlsEnabled();
Set<Map.Entry<ServiceType, Integer>> aServices = null;
Optional<String> alternateAddress = coreContext.alternateAddress();
String aHost = null;
if (alternateAddress.isPresent()) {
AlternateAddress aa = ni.alternateAddresses().get(alternateAddress.get());
aHost = aa.hostname();
aServices = tls ? aa.sslServices().entrySet() : aa.services().entrySet();
}
if (isNullOrEmpty(aServices)) {
aServices = tls ? ni.sslServices().entrySet() : ni.services().entrySet();
}
final String alternateHost = aHost;
final Set<Map.Entry<ServiceType, Integer>> services = aServices;
Flux<Void> serviceRemoveFlux = Flux.fromIterable(Arrays.asList(ServiceType.values())).filter(s -> {
for (Map.Entry<ServiceType, Integer> inConfig : services) {
if (inConfig.getKey() == s) {
return false;
}
}
return true;
}).flatMap(s -> removeServiceFrom(ni.identifier(), s, s.scope() == ServiceScope.BUCKET ? Optional.of(bc.name()) : Optional.empty()).onErrorResume(throwable -> {
eventBus.publish(new ServiceReconfigurationFailedEvent(coreContext, ni.hostname(), s, throwable));
return Mono.empty();
}));
Flux<Void> serviceAddFlux = Flux.fromIterable(services).flatMap(s -> ensureServiceAt(ni.identifier(), s.getKey(), s.getValue(), s.getKey().scope() == ServiceScope.BUCKET ? Optional.of(bc.name()) : Optional.empty(), Optional.ofNullable(alternateHost)).onErrorResume(throwable -> {
eventBus.publish(new ServiceReconfigurationFailedEvent(coreContext, ni.hostname(), s.getKey(), throwable));
return Mono.empty();
}));
return Flux.merge(serviceAddFlux, serviceRemoveFlux);
})).then();
}
use of com.couchbase.client.core.config.BucketConfig in project couchbase-jvm-clients by couchbase.
the class Core method reconfigure.
/**
* Reconfigures the SDK topology to align with the current server configuration.
*
* <p>When reconfigure is called, it will grab a current configuration and then add/remove
* nodes/services to mirror the current topology and configuration settings.</p>
*
* <p>This is a eventually consistent process, so in-flight operations might still be rescheduled
* and then picked up later (or cancelled, depending on the strategy). For those coming from 1.x,
* it works very similar.</p>
*/
private void reconfigure() {
if (reconfigureInProgress.compareAndSet(false, true)) {
final ClusterConfig configForThisAttempt = currentConfig;
if (configForThisAttempt.bucketConfigs().isEmpty() && configForThisAttempt.globalConfig() == null) {
reconfigureDisconnectAll();
return;
}
final long start = System.nanoTime();
Flux<BucketConfig> bucketConfigFlux = Flux.just(configForThisAttempt).flatMap(cc -> Flux.fromIterable(cc.bucketConfigs().values()));
reconfigureBuckets(bucketConfigFlux).then(reconfigureGlobal(configForThisAttempt.globalConfig())).then(Mono.defer(() -> Flux.fromIterable(new ArrayList<>(nodes)).flatMap(n -> maybeRemoveNode(n, configForThisAttempt)).then())).subscribe(v -> {
}, e -> {
clearReconfigureInProgress();
eventBus.publish(new ReconfigurationErrorDetectedEvent(context(), e));
}, () -> {
clearReconfigureInProgress();
eventBus.publish(new ReconfigurationCompletedEvent(Duration.ofNanos(System.nanoTime() - start), coreContext));
});
} else {
moreConfigsPending.set(true);
eventBus.publish(new ReconfigurationIgnoredEvent(coreContext));
}
}
use of com.couchbase.client.core.config.BucketConfig in project couchbase-jvm-clients by couchbase.
the class ClusterManagerBucketLoaderIntegrationTest method loadConfigViaClusterManagerHttp.
/**
* This is a very simplistic test that makes sure that we can "round trip" in the
* {@link ClusterManagerBucketLoader} by grabbing a JSON decodable config through the full stack.
*/
@Test
// @Disabled
void loadConfigViaClusterManagerHttp() {
TestNodeConfig config = config().firstNodeWith(Services.MANAGER).get();
Core core = Core.create(env, authenticator(), seedNodes());
ClusterManagerBucketLoader loader = new ClusterManagerBucketLoader(core);
int port = config.ports().get(Services.MANAGER);
ProposedBucketConfigContext ctx = loader.load(new NodeIdentifier(config.hostname(), port), port, config().bucketname(), Optional.empty()).block();
BucketConfig loaded = BucketConfigParser.parse(ctx.config(), env, ctx.origin());
assertNotNull(loaded);
assertEquals(config().bucketname(), loaded.name());
core.shutdown().block();
}
use of com.couchbase.client.core.config.BucketConfig in project couchbase-jvm-clients by couchbase.
the class ReactiveBatchHelper method existsBytes.
/**
* Performs the bulk logic of fetching a config and splitting up the observe requests.
*
* @param collection the collection on which the query should be performed.
* @param ids the list of ids which should be checked.
* @return a flux of all
*/
private static Flux<byte[]> existsBytes(final Collection collection, final java.util.Collection<String> ids) {
final Core core = collection.core();
final CoreEnvironment env = core.context().environment();
BucketConfig config = core.clusterConfig().bucketConfig(collection.bucketName());
if (core.configurationProvider().bucketConfigLoadInProgress() || config == null) {
// and then try again. In a steady state this should not happen.
return Mono.delay(Duration.ofMillis(100), env.scheduler()).flatMapMany(ign -> existsBytes(collection, ids));
}
long start = System.nanoTime();
if (!(config instanceof CouchbaseBucketConfig)) {
throw new IllegalStateException("Only couchbase (and ephemeral) buckets are supported at this point!");
}
Map<NodeIdentifier, Map<byte[], Short>> nodeEntries = new HashMap<>(config.nodes().size());
for (NodeInfo node : config.nodes()) {
nodeEntries.put(node.identifier(), new HashMap<>(ids.size() / config.nodes().size()));
}
CouchbaseBucketConfig cbc = (CouchbaseBucketConfig) config;
CollectionIdentifier ci = new CollectionIdentifier(collection.bucketName(), Optional.of(collection.scopeName()), Optional.of(collection.name()));
for (String id : ids) {
byte[] encodedId = id.getBytes(StandardCharsets.UTF_8);
int partitionId = KeyValueLocator.partitionForKey(encodedId, cbc.numberOfPartitions());
int nodeId = cbc.nodeIndexForActive(partitionId, false);
NodeInfo nodeInfo = cbc.nodeAtIndex(nodeId);
nodeEntries.get(nodeInfo.identifier()).put(encodedId, (short) partitionId);
}
List<Mono<MultiObserveViaCasResponse>> responses = new ArrayList<>(nodeEntries.size());
List<MultiObserveViaCasRequest> requests = new ArrayList<>(nodeEntries.size());
for (Map.Entry<NodeIdentifier, Map<byte[], Short>> node : nodeEntries.entrySet()) {
if (node.getValue().isEmpty()) {
// service enabled and 2) have keys that we need to fetch
continue;
}
MultiObserveViaCasRequest request = new MultiObserveViaCasRequest(env.timeoutConfig().kvTimeout(), core.context(), env.retryStrategy(), ci, node.getKey(), node.getValue(), PMGET_PREDICATE);
core.send(request);
requests.add(request);
responses.add(Reactor.wrap(request, request.response(), true));
}
return Flux.merge(responses).flatMap(response -> Flux.fromIterable(response.observed().keySet())).onErrorMap(throwable -> {
BatchErrorContext ctx = new BatchErrorContext(Collections.unmodifiableList(requests));
return new BatchHelperFailureException("Failed to perform BatchHelper bulk operation", throwable, ctx);
}).doOnComplete(() -> core.context().environment().eventBus().publish(new BatchHelperExistsCompletedEvent(Duration.ofNanos(System.nanoTime() - start), new BatchErrorContext(Collections.unmodifiableList(requests)))));
}
use of com.couchbase.client.core.config.BucketConfig 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;
}
}
Aggregations