0
# BookKeeper Testing Utilities
1
2
Enhanced testing components providing specialized test clients, in-memory statistics providers, and server testing infrastructure for comprehensive BookKeeper testing scenarios beyond basic mocking.
3
4
## Capabilities
5
6
### BookKeeperTestClient
7
8
Enhanced BookKeeper client with access to internal components and specialized testing methods for integration testing scenarios.
9
10
```java { .api }
11
class BookKeeperTestClient extends BookKeeper {
12
// Constructors
13
BookKeeperTestClient(ClientConfiguration conf) throws InterruptedException, BKException;
14
BookKeeperTestClient(ClientConfiguration conf, TestStatsProvider statsProvider) throws InterruptedException, BKException;
15
BookKeeperTestClient(ClientConfiguration conf, ZooKeeper zkc) throws InterruptedException, BKException;
16
17
// Internal Component Access
18
ZooKeeper getZkHandle();
19
ClientConfiguration getConf();
20
BookieClient getBookieClient();
21
TestStatsProvider getTestStatsProvider();
22
23
// Testing Operations
24
void waitForReadOnlyBookie(BookieId id) throws Exception;
25
void waitForWritableBookie(BookieId id) throws Exception;
26
void readBookiesBlocking() throws BKException;
27
}
28
```
29
30
### TestStatsProvider
31
32
In-memory statistics provider for testing with comprehensive metrics collection and analysis capabilities.
33
34
```java { .api }
35
class TestStatsProvider implements StatsProvider {
36
// StatsProvider Interface
37
void start(Configuration conf);
38
void stop();
39
TestStatsLogger getStatsLogger(String scope);
40
String getStatsName(String... statsComponents);
41
42
// Access Methods
43
TestOpStatsLogger getOpStatsLogger(String path);
44
TestCounter getCounter(String path);
45
Gauge<? extends Number> getGauge(String path);
46
void forEachOpStatLogger(BiConsumer<String, TestOpStatsLogger> f);
47
void clear();
48
}
49
```
50
51
### TestCounter
52
53
In-memory counter for tracking numeric metrics during testing.
54
55
```java { .api }
56
class TestCounter implements Counter {
57
// Counter Operations
58
void clear();
59
void inc();
60
void dec();
61
void addCount(long delta);
62
void addLatency(long eventLatency, TimeUnit unit);
63
64
// Value Access
65
Long get();
66
Long getMax();
67
}
68
```
69
70
### TestOpStatsLogger
71
72
In-memory operation statistics logger for tracking operation latencies and success/failure rates.
73
74
```java { .api }
75
class TestOpStatsLogger implements OpStatsLogger {
76
// Event Registration
77
void registerFailedEvent(long eventLatency, TimeUnit unit);
78
void registerSuccessfulEvent(long eventLatency, TimeUnit unit);
79
synchronized void registerSuccessfulValue(long value);
80
synchronized void registerFailedValue(long value);
81
82
// Statistics Access
83
OpStatsData toOpStatsData();
84
synchronized void clear();
85
synchronized double getSuccessAverage();
86
synchronized long getSuccessCount();
87
}
88
```
89
90
### TestStatsLogger
91
92
In-memory hierarchical statistics logger for organizing metrics by scope.
93
94
```java { .api }
95
class TestStatsLogger implements StatsLogger {
96
// Metric Creation
97
OpStatsLogger getOpStatsLogger(String name);
98
Counter getCounter(String name);
99
Gauge<? extends Number> getGauge(String name);
100
101
// Gauge Management
102
<T extends Number> void registerGauge(String name, Gauge<T> gauge);
103
<T extends Number> void unregisterGauge(String name, Gauge<T> gauge);
104
105
// Scope Management
106
StatsLogger scope(String name);
107
void removeScope(String name, StatsLogger statsLogger);
108
109
// Thread-Scoped Metrics
110
OpStatsLogger getThreadScopedOpStatsLogger(String name);
111
Counter getThreadScopedCounter(String name);
112
}
113
```
114
115
## Types
116
117
### Statistics Interfaces
118
119
```java { .api }
120
interface StatsProvider {
121
void start(Configuration conf);
122
void stop();
123
StatsLogger getStatsLogger(String scope);
124
String getStatsName(String... statsComponents);
125
}
126
127
interface StatsLogger {
128
OpStatsLogger getOpStatsLogger(String name);
129
Counter getCounter(String name);
130
<T extends Number> Gauge<T> getGauge(String name);
131
<T extends Number> void registerGauge(String name, Gauge<T> gauge);
132
<T extends Number> void unregisterGauge(String name, Gauge<T> gauge);
133
StatsLogger scope(String name);
134
void removeScope(String name, StatsLogger statsLogger);
135
OpStatsLogger getThreadScopedOpStatsLogger(String name);
136
Counter getThreadScopedCounter(String name);
137
}
138
139
interface OpStatsLogger {
140
void registerFailedEvent(long eventLatency, TimeUnit unit);
141
void registerSuccessfulEvent(long eventLatency, TimeUnit unit);
142
void registerSuccessfulValue(long value);
143
void registerFailedValue(long value);
144
OpStatsData toOpStatsData();
145
void clear();
146
}
147
148
interface Counter {
149
void clear();
150
void inc();
151
void dec();
152
void addCount(long delta);
153
void addLatency(long eventLatency, TimeUnit unit);
154
Long get();
155
}
156
157
interface Gauge<T extends Number> {
158
T getDefaultValue();
159
T getSample();
160
}
161
```
162
163
### BookKeeper Client Types
164
165
```java { .api }
166
class ClientConfiguration extends AbstractConfiguration {
167
// BookKeeper client configuration
168
// (Part of BookKeeper API)
169
}
170
171
class BookieClient {
172
// Internal BookKeeper bookie client
173
// (Part of BookKeeper internal API)
174
}
175
176
class BookieId {
177
// Bookie identifier
178
// (Part of BookKeeper API)
179
}
180
```
181
182
## Usage Examples
183
184
### Enhanced Client Testing
185
186
```java
187
import org.apache.bookkeeper.client.BookKeeperTestClient;
188
import org.apache.bookkeeper.client.TestStatsProvider;
189
import org.apache.bookkeeper.conf.ClientConfiguration;
190
191
// Create client configuration
192
ClientConfiguration conf = new ClientConfiguration();
193
conf.setMetadataServiceUri("zk+null://localhost/ledgers");
194
195
// Create test client with custom statistics provider
196
TestStatsProvider statsProvider = new TestStatsProvider();
197
BookKeeperTestClient testClient = new BookKeeperTestClient(conf, statsProvider);
198
199
// Access internal components for testing
200
ZooKeeper zkHandle = testClient.getZkHandle();
201
BookieClient bookieClient = testClient.getBookieClient();
202
203
// Perform operations and collect statistics
204
LedgerHandle ledger = testClient.createLedger(DigestType.CRC32, "password".getBytes());
205
ledger.addEntry("test data".getBytes());
206
207
// Analyze collected statistics
208
TestStatsProvider.TestOpStatsLogger createStats = statsProvider.getOpStatsLogger("ledger.create");
209
System.out.println("Create operations: " + createStats.getSuccessCount());
210
211
testClient.close();
212
```
213
214
### Statistics Collection and Analysis
215
216
```java
217
TestStatsProvider statsProvider = new TestStatsProvider();
218
219
// Create loggers for different scopes
220
TestStatsProvider.TestStatsLogger rootLogger = statsProvider.getStatsLogger("bookkeeper");
221
TestStatsProvider.TestStatsLogger clientLogger = rootLogger.scope("client");
222
223
// Create metrics
224
TestStatsProvider.TestOpStatsLogger readOps = clientLogger.getOpStatsLogger("read");
225
TestStatsProvider.TestCounter totalRequests = clientLogger.getCounter("requests.total");
226
227
// Simulate operations
228
totalRequests.inc();
229
readOps.registerSuccessfulEvent(50, TimeUnit.MILLISECONDS);
230
totalRequests.inc();
231
readOps.registerSuccessfulEvent(75, TimeUnit.MILLISECONDS);
232
totalRequests.inc();
233
readOps.registerFailedEvent(100, TimeUnit.MILLISECONDS);
234
235
// Analyze results
236
System.out.println("Total requests: " + totalRequests.get());
237
System.out.println("Read success count: " + readOps.getSuccessCount());
238
System.out.println("Average read latency: " + readOps.getSuccessAverage() + "ms");
239
240
// Clear metrics for next test
241
statsProvider.clear();
242
```
243
244
### Bookie State Testing
245
246
```java
247
BookKeeperTestClient testClient = new BookKeeperTestClient(conf);
248
249
// Test bookie availability
250
BookieId bookieId = new BookieId("127.0.0.1", 3181);
251
252
// Wait for bookie to become read-only
253
Future<?> readOnlyFuture = testClient.waitForReadOnlyBookie(bookieId);
254
readOnlyFuture.get(30, TimeUnit.SECONDS);
255
256
// Wait for bookie to become writable again
257
Future<?> writableFuture = testClient.waitForWritableBookie(bookieId);
258
writableFuture.get(30, TimeUnit.SECONDS);
259
260
// Force reading bookie information
261
testClient.readBookiesBlocking();
262
263
testClient.close();
264
```
265
266
### Custom Gauge Testing
267
268
```java
269
TestStatsProvider statsProvider = new TestStatsProvider();
270
TestStatsProvider.TestStatsLogger logger = statsProvider.getStatsLogger("test");
271
272
// Register custom gauge
273
AtomicLong queueSize = new AtomicLong(0);
274
Gauge<Long> queueGauge = new Gauge<Long>() {
275
public Long getDefaultValue() { return 0L; }
276
public Long getSample() { return queueSize.get(); }
277
};
278
279
logger.registerGauge("queue.size", queueGauge);
280
281
// Simulate queue operations
282
queueSize.set(10);
283
Gauge<? extends Number> retrievedGauge = logger.getGauge("queue.size");
284
System.out.println("Queue size: " + retrievedGauge.getSample());
285
286
// Clean up
287
logger.unregisterGauge("queue.size", queueGauge);
288
```
289
290
### Integration Testing with Multiple Clients
291
292
```java
293
public class MultiClientTest {
294
private List<BookKeeperTestClient> clients = new ArrayList<>();
295
private TestStatsProvider globalStats = new TestStatsProvider();
296
297
@Before
298
public void setUp() throws Exception {
299
ClientConfiguration conf = new ClientConfiguration();
300
conf.setMetadataServiceUri("zk+null://localhost/ledgers");
301
302
// Create multiple test clients
303
for (int i = 0; i < 3; i++) {
304
BookKeeperTestClient client = new BookKeeperTestClient(conf, globalStats);
305
clients.add(client);
306
}
307
}
308
309
@Test
310
public void testConcurrentOperations() throws Exception {
311
// Perform concurrent operations with different clients
312
List<CompletableFuture<Void>> futures = new ArrayList<>();
313
314
for (int i = 0; i < clients.size(); i++) {
315
final int clientIndex = i;
316
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
317
try {
318
BookKeeperTestClient client = clients.get(clientIndex);
319
LedgerHandle ledger = client.createLedger(DigestType.CRC32, "password".getBytes());
320
321
for (int j = 0; j < 10; j++) {
322
ledger.addEntry(("data-" + clientIndex + "-" + j).getBytes());
323
}
324
325
ledger.close();
326
} catch (Exception e) {
327
throw new RuntimeException(e);
328
}
329
});
330
futures.add(future);
331
}
332
333
// Wait for all operations to complete
334
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
335
336
// Analyze global statistics
337
globalStats.forEachOpStatLogger((path, opStats) -> {
338
System.out.println(path + ": " + opStats.getSuccessCount() + " operations");
339
});
340
}
341
342
@After
343
public void tearDown() throws Exception {
344
for (BookKeeperTestClient client : clients) {
345
client.close();
346
}
347
}
348
}
349
```
350
351
### Thread-Scoped Metrics Testing
352
353
```java
354
TestStatsProvider statsProvider = new TestStatsProvider();
355
TestStatsProvider.TestStatsLogger logger = statsProvider.getStatsLogger("thread-test");
356
357
// Create thread-scoped metrics
358
ExecutorService executor = Executors.newFixedThreadPool(3);
359
List<Future<?>> futures = new ArrayList<>();
360
361
for (int i = 0; i < 3; i++) {
362
Future<?> future = executor.submit(() -> {
363
// Each thread gets its own scoped metrics
364
TestStatsProvider.TestOpStatsLogger threadOpStats = logger.getThreadScopedOpStatsLogger("operation");
365
TestStatsProvider.TestCounter threadCounter = (TestStatsProvider.TestCounter) logger.getThreadScopedCounter("requests");
366
367
// Perform operations
368
for (int j = 0; j < 5; j++) {
369
threadCounter.inc();
370
threadOpStats.registerSuccessfulEvent(j * 10, TimeUnit.MILLISECONDS);
371
}
372
});
373
futures.add(future);
374
}
375
376
// Wait for completion
377
for (Future<?> future : futures) {
378
future.get();
379
}
380
381
executor.shutdown();
382
```