use of com.couchbase.client.core.endpoint.Endpoint in project couchbase-jvm-clients by couchbase.
the class PooledService method connect.
@Override
public synchronized void connect() {
if (state() == ServiceState.DISCONNECTED && !disconnected.get()) {
serviceContext.environment().eventBus().publish(new ServiceConnectInitiatedEvent(serviceContext, serviceConfig.minEndpoints()));
for (int i = 0; i < serviceConfig.minEndpoints(); i++) {
Endpoint endpoint = createEndpoint();
endpointStates.register(endpoint, endpoint);
endpoint.connect();
endpoints.add(endpoint);
}
}
}
use of com.couchbase.client.core.endpoint.Endpoint in project couchbase-jvm-clients by couchbase.
the class PooledService method cleanIdleConnections.
/**
* Go through the connections and clean up all the idle connections.
* <p>
* Note that we explicitly do not make any clean up attempts on the {@link #reservedEndpoints}. They will either come
* into our endpoint pool when connected, or fall out of the pool when they are disconnected immediately. We only need
* to take them into account when checking how many endpoints we have flying around to clean up at max.
*/
private synchronized void cleanIdleConnections() {
if (disconnected.get()) {
return;
}
final List<Endpoint> endpoints = new ArrayList<>(this.endpoints);
Collections.shuffle(endpoints);
for (Endpoint endpoint : endpoints) {
if ((this.endpoints.size() + this.reservedEndpoints.size()) == serviceConfig.minEndpoints()) {
break;
}
long lastResponseReceived = endpoint.lastResponseReceived();
long actualIdleTime;
if (lastResponseReceived != 0) {
actualIdleTime = System.nanoTime() - endpoint.lastResponseReceived();
} else {
// If we did not receive a last response timestamp, it could be the case that a socket is
// connected but no request has been sent into it yet. If this is the case, take the timestamp
// when the socket got last connected as a reference point to determine if it is idle.
long lastConnected = endpoint.lastConnectedAt();
if (lastConnected != 0) {
actualIdleTime = System.nanoTime() - lastConnected;
} else {
// No last connected timestamp, so the endpoint isn't even fully connected yet
continue;
}
}
// we also check if an endpoint received a hard disconnect signal and is still lingering around
boolean receivedDisconnect = endpoint.receivedDisconnectSignal();
boolean idleTooLong = endpoint.outstandingRequests() == 0 && actualIdleTime >= serviceConfig.idleTime().toNanos();
if (receivedDisconnect || idleTooLong) {
this.endpoints.remove(endpoint);
endpointStates.deregister(endpoint);
if (!receivedDisconnect) {
endpoint.disconnect();
}
publishIdleEndpointRemovedEvent(endpoint, actualIdleTime);
}
}
scheduleCleanIdleConnections();
}
use of com.couchbase.client.core.endpoint.Endpoint in project couchbase-jvm-clients by couchbase.
the class PooledService method connectReservedEndpoint.
/**
* Connect the reserved endpoint and dispatch the request into it if possible.
* <p>
* Note that there are two synchronized sections in this method, because the subscription callback works on
* a different thread.
*
* @param request the request that needs to bee dispatched.
*/
private synchronized <R extends Request<? extends Response>> void connectReservedEndpoint(final R request) {
if (!disconnected.get()) {
Endpoint endpoint = createEndpoint();
endpointStates.register(endpoint, endpoint);
endpoint.states().skipUntil(s -> s == EndpointState.CONNECTING).filter(s -> s == EndpointState.CONNECTED || s == EndpointState.DISCONNECTED).takeUntil(s -> s == EndpointState.CONNECTED || s == EndpointState.DISCONNECTED).publishOn(context().environment().scheduler()).subscribe(s -> {
synchronized (PooledService.this) {
reservedEndpoints.remove(endpoint);
if (disconnected.get()) {
endpoint.disconnect();
endpointStates.deregister(endpoint);
RetryOrchestrator.maybeRetry(serviceContext, request, RetryReason.ENDPOINT_NOT_AVAILABLE);
} else {
endpoints.add(endpoint);
if (s == EndpointState.CONNECTED) {
endpoint.send(request);
} else if (s == EndpointState.DISCONNECTED) {
RetryOrchestrator.maybeRetry(serviceContext, request, RetryReason.ENDPOINT_NOT_AVAILABLE);
}
}
}
});
endpoint.connect();
reservedEndpoints.add(endpoint);
}
}
use of com.couchbase.client.core.endpoint.Endpoint in project couchbase-jvm-clients by couchbase.
the class PooledServiceTest method dispatchesDirectlyIfSlotAvailable.
@Test
void dispatchesDirectlyIfSlotAvailable() {
int minEndpoints = 0;
Endpoint mock1 = mock(Endpoint.class);
when(mock1.state()).thenReturn(EndpointState.CONNECTED);
DirectProcessor<EndpointState> states = DirectProcessor.create();
when(mock1.states()).thenReturn(states);
when(mock1.outstandingRequests()).thenReturn(0L);
final List<Endpoint> mocks = Collections.singletonList(mock1);
final AtomicInteger invocation = new AtomicInteger();
MockedService service = new MockedService(new MockedServiceConfig(minEndpoints, 2, Duration.ofMillis(500), false), () -> mocks.get(invocation.getAndIncrement()), new FirstEndpointSelectionStrategy());
service.connect();
assertTrue(service.trackedEndpoints().isEmpty());
NoopRequest request = new NoopRequest(Duration.ofSeconds(1), serviceContext, BestEffortRetryStrategy.INSTANCE, CollectionIdentifier.fromDefault("bucket"));
service.send(request);
// Simulate the connecting and connected
states.onNext(EndpointState.CONNECTING);
states.onNext(EndpointState.CONNECTED);
waitUntilCondition(() -> !service.trackedEndpoints.isEmpty());
assertEquals(1, service.trackedEndpoints().size());
assertEquals(0, request.context().retryAttempts());
waitUntilCondition(() -> {
Collection<Invocation> invocations = Mockito.mockingDetails(mock1).getInvocations();
for (Invocation inv : invocations) {
if (inv.getMethod().getName().equals("send")) {
if (inv.getArgument(0) == request) {
return true;
}
}
}
return false;
});
verify(mock1, times(1)).send(request);
}
use of com.couchbase.client.core.endpoint.Endpoint in project couchbase-jvm-clients by couchbase.
the class PooledServiceTest method sendsRequestIntoEndpoint.
/**
* Most basic test to check if we can dispatch into the endpoint.
*/
@Test
@SuppressWarnings({ "unchecked" })
void sendsRequestIntoEndpoint() {
int minEndpoints = 2;
Endpoint mock1 = mock(Endpoint.class);
when(mock1.state()).thenReturn(EndpointState.CONNECTED);
when(mock1.states()).thenReturn(DirectProcessor.create());
when(mock1.outstandingRequests()).thenReturn(0L);
Endpoint mock2 = mock(Endpoint.class);
when(mock2.state()).thenReturn(EndpointState.CONNECTED);
when(mock2.states()).thenReturn(DirectProcessor.create());
final List<Endpoint> mocks = Arrays.asList(mock1, mock2);
final AtomicInteger invocation = new AtomicInteger();
MockedService service = new MockedService(new MockedServiceConfig(minEndpoints, 2), () -> mocks.get(invocation.getAndIncrement()), new FirstEndpointSelectionStrategy());
service.connect();
Request<? extends Response> request = mock(Request.class);
service.send(request);
verify(mock1, times(1)).send(request);
verify(mock2, never()).send(request);
}
Aggregations