Skip to content

Commit d92bd20

Browse files
authored
[client] new API to check if Bookkeeper client is connected to metadata service (#4342)
# Motivation when zookeeper disconnect, use bookkeeper api to create new ledger will fail. add a new api to check metadata driver is available, if not available, use current ledger continue and don't rollover ledger ## ** tip ** Although it may not solve all issues since it's not atomic, it will reduce the losses caused by zk unavailability. # Modification add a new api named isDriverMetadataServiceAvailable
1 parent 4ca020a commit d92bd20

File tree

7 files changed

+102
-0
lines changed

7 files changed

+102
-0
lines changed

bookkeeper-server/src/main/java/org/apache/bookkeeper/client/BookKeeper.java

+5
Original file line numberDiff line numberDiff line change
@@ -1609,6 +1609,11 @@ public CompletableFuture<LedgerMetadata> getLedgerMetadata(long ledgerId) {
16091609
});
16101610
}
16111611

1612+
@Override
1613+
public CompletableFuture<Boolean> isDriverMetadataServiceAvailable() {
1614+
return metadataDriver.isMetadataServiceAvailable();
1615+
}
1616+
16121617
private final ClientContext clientCtx = new ClientContext() {
16131618
@Override
16141619
public ClientInternalConf getConf() {

bookkeeper-server/src/main/java/org/apache/bookkeeper/client/api/BookKeeper.java

+7
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ static BookKeeperBuilder newBuilder(final ClientConfiguration clientConfiguratio
8181
*/
8282
CompletableFuture<LedgerMetadata> getLedgerMetadata(long ledgerId);
8383

84+
/**
85+
* Return driver metadata service is available.
86+
*
87+
* @return the metadata service is available.
88+
*/
89+
CompletableFuture<Boolean> isDriverMetadataServiceAvailable();
90+
8491
/**
8592
* Close the client and release every resource.
8693
*

bookkeeper-server/src/main/java/org/apache/bookkeeper/meta/MetadataClientDriver.java

+9
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,13 @@ interface SessionStateListener {
114114
default CompletableFuture<Boolean> isHealthCheckEnabled() {
115115
return FutureUtils.value(true);
116116
}
117+
118+
/**
119+
* Return driver metadata service is available.
120+
*
121+
* @return the metadata service is available.
122+
*/
123+
default CompletableFuture<Boolean> isMetadataServiceAvailable() {
124+
return FutureUtils.value(true);
125+
}
117126
}

bookkeeper-server/src/main/java/org/apache/bookkeeper/meta/zk/ZKMetadataClientDriver.java

+5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import com.google.common.annotations.VisibleForTesting;
2222
import java.util.Optional;
23+
import java.util.concurrent.CompletableFuture;
2324
import java.util.concurrent.ScheduledExecutorService;
2425
import lombok.extern.slf4j.Slf4j;
2526
import org.apache.bookkeeper.conf.ClientConfiguration;
@@ -111,4 +112,8 @@ public void setSessionStateListener(SessionStateListener sessionStateListener) {
111112
}
112113
});
113114
}
115+
116+
public CompletableFuture<Boolean> isMetadataServiceAvailable() {
117+
return CompletableFuture.completedFuture(metadataServiceAvailable);
118+
}
114119
}

bookkeeper-server/src/main/java/org/apache/bookkeeper/meta/zk/ZKMetadataDriverBase.java

+23
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import java.io.IOException;
2929
import java.net.URI;
30+
import java.util.Collections;
3031
import java.util.List;
3132
import java.util.Optional;
3233
import java.util.concurrent.CompletableFuture;
@@ -53,6 +54,7 @@
5354
import org.apache.zookeeper.AsyncCallback;
5455
import org.apache.zookeeper.CreateMode;
5556
import org.apache.zookeeper.KeeperException;
57+
import org.apache.zookeeper.Watcher;
5658
import org.apache.zookeeper.ZooKeeper;
5759
import org.apache.zookeeper.data.ACL;
5860
import org.apache.zookeeper.data.Stat;
@@ -64,6 +66,9 @@
6466
public class ZKMetadataDriverBase implements AutoCloseable {
6567

6668
protected static final String SCHEME = "zk";
69+
70+
protected volatile boolean metadataServiceAvailable;
71+
6772
private static final int ZK_CLIENT_WAIT_FOR_SHUTDOWN_TIMEOUT_MS = 5000;
6873

6974
public static String getZKServersFromServiceUri(URI uri) {
@@ -179,6 +184,7 @@ protected void initialize(AbstractConfiguration<?> conf,
179184
// if an external zookeeper is added, use the zookeeper instance
180185
this.zk = (ZooKeeper) (optionalCtx.get());
181186
this.ownZKHandle = false;
187+
this.metadataServiceAvailable = true;
182188
} else {
183189
final String metadataServiceUriStr;
184190
try {
@@ -212,6 +218,12 @@ protected void initialize(AbstractConfiguration<?> conf,
212218
.sessionTimeoutMs(conf.getZkTimeout())
213219
.operationRetryPolicy(zkRetryPolicy)
214220
.requestRateLimit(conf.getZkRequestRateLimit())
221+
.watchers(Collections.singleton(watchedEvent -> {
222+
if (log.isDebugEnabled()) {
223+
log.debug("Got ZK session watch event: {}", watchedEvent);
224+
}
225+
handleState(watchedEvent.getState());
226+
}))
215227
.statsLogger(statsLogger)
216228
.build();
217229

@@ -247,6 +259,17 @@ protected void initialize(AbstractConfiguration<?> conf,
247259
acls);
248260
}
249261

262+
private void handleState(Watcher.Event.KeeperState zkClientState) {
263+
switch (zkClientState) {
264+
case Expired:
265+
case Disconnected:
266+
this.metadataServiceAvailable = false;
267+
break;
268+
default:
269+
this.metadataServiceAvailable = true;
270+
}
271+
}
272+
250273
public LayoutManager getLayoutManager() {
251274
return layoutManager;
252275
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
*
3+
* Licensed to the Apache Software Foundation (ASF) under one
4+
* or more contributor license agreements. See the NOTICE file
5+
* distributed with this work for additional information
6+
* regarding copyright ownership. The ASF licenses this file
7+
* to you under the Apache License, Version 2.0 (the
8+
* "License"); you may not use this file except in compliance
9+
* with the License. You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing,
14+
* software distributed under the License is distributed on an
15+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
* KIND, either express or implied. See the License for the
17+
* specific language governing permissions and limitations
18+
* under the License.
19+
*
20+
*/
21+
package org.apache.bookkeeper.client.api;
22+
23+
import java.util.concurrent.CountDownLatch;
24+
import java.util.concurrent.TimeUnit;
25+
import org.apache.bookkeeper.conf.ClientConfiguration;
26+
import org.apache.bookkeeper.test.BookKeeperClusterTestCase;
27+
import org.awaitility.Awaitility;
28+
import org.junit.Test;
29+
30+
/**
31+
* Bookkeeper Client API driver metadata service available test.
32+
*/
33+
public class DriverMetadataServiceAvailableTest extends BookKeeperClusterTestCase {
34+
35+
public DriverMetadataServiceAvailableTest() {
36+
super(3);
37+
}
38+
39+
@Test
40+
public void testDriverMetadataServiceAvailable()
41+
throws Exception {
42+
ClientConfiguration conf = new ClientConfiguration();
43+
conf.setMetadataServiceUri(zkUtil.getMetadataServiceUri());
44+
conf.setZkTimeout(3000);
45+
try (BookKeeper bkc = BookKeeper.newBuilder(conf).build()) {
46+
Awaitility.await().until(() -> bkc.isDriverMetadataServiceAvailable().get());
47+
zkUtil.sleepCluster(5, TimeUnit.SECONDS, new CountDownLatch(1));
48+
Awaitility.await().until(() -> !bkc.isDriverMetadataServiceAvailable().get());
49+
Awaitility.await().until(() -> bkc.isDriverMetadataServiceAvailable().get());
50+
}
51+
}
52+
}

bookkeeper-server/src/test/java/org/apache/bookkeeper/meta/zk/ZKMetadataDriverTestBase.java

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public void setup(AbstractConfiguration<?> conf) throws Exception {
5757
when(mockZkBuilder.operationRetryPolicy(any(RetryPolicy.class)))
5858
.thenReturn(mockZkBuilder);
5959
when(mockZkBuilder.requestRateLimit(anyDouble())).thenReturn(mockZkBuilder);
60+
when(mockZkBuilder.watchers(any())).thenReturn(mockZkBuilder);
6061
when(mockZkBuilder.statsLogger(any(StatsLogger.class))).thenReturn(mockZkBuilder);
6162

6263
this.mockZkc = mock(ZooKeeperClient.class);

0 commit comments

Comments
 (0)