Skip to content

Commit 5870922

Browse files
authored
[fix]Wrong error code(-107) of opening a deleted ledger (#4657)
1 parent 2789316 commit 5870922

File tree

9 files changed

+143
-8
lines changed

9 files changed

+143
-8
lines changed

bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/BookieException.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ public static BookieException create(int code) {
6969
return new MetadataStoreException();
7070
case Code.UnknownBookieIdException:
7171
return new UnknownBookieIdException();
72+
case Code.LedgerFencedAndDeletedException:
73+
return new LedgerFencedAndDeletedException();
7274
case Code.DataUnknownException:
7375
return new DataUnknownException();
7476
default:
@@ -95,6 +97,7 @@ public interface Code {
9597
int CookieExistsException = -109;
9698
int EntryLogMetadataMapException = -110;
9799
int DataUnknownException = -111;
100+
int LedgerFencedAndDeletedException = -112;
98101
}
99102

100103
public int getCode() {
@@ -199,6 +202,15 @@ public LedgerFencedException() {
199202
}
200203
}
201204

205+
/**
206+
* Signals that a ledger has been fenced in a bookie. No more entries can be appended to that ledger.
207+
*/
208+
public static class LedgerFencedAndDeletedException extends BookieException {
209+
public LedgerFencedAndDeletedException() {
210+
super(Code.LedgerFencedException);
211+
}
212+
}
213+
202214
/**
203215
* Signals that a ledger's operation has been rejected by an internal component because of the resource saturation.
204216
*/

bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/HandleFactoryImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public LedgerDescriptor getHandle(final long ledgerId, final byte[] masterKey, b
5757

5858
if (handle == null) {
5959
if (!journalReplay && recentlyFencedAndDeletedLedgers.getIfPresent(ledgerId) != null) {
60-
throw BookieException.create(BookieException.Code.LedgerFencedException);
60+
throw BookieException.create(BookieException.Code.LedgerFencedAndDeletedException);
6161
}
6262
handle = LedgerDescriptor.create(masterKey, ledgerId, ledgerStorage);
6363
ledgers.putIfAbsent(ledgerId, handle);

bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,6 +1194,11 @@ private void writeAndFlush(final Channel channel,
11941194
completion.setOutstanding();
11951195
}
11961196
} else {
1197+
try {
1198+
future.get();
1199+
} catch (Exception ex) {
1200+
LOG.warn("Failed to request to the bookie: {}", bookieId, ex);
1201+
}
11971202
nettyOpLogger.registerFailedEvent(MathUtils.elapsedNanos(startTime), TimeUnit.NANOSECONDS);
11981203
errorOut(key);
11991204
}

bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/ReadEntryProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ protected void processPacket() {
9494
handleReadResultForFenceRead(fenceResult, data, startTimeNanos);
9595
return;
9696
}
97-
} catch (Bookie.NoLedgerException e) {
97+
} catch (Bookie.NoLedgerException | BookieException.LedgerFencedAndDeletedException e) {
9898
if (LOG.isDebugEnabled()) {
9999
LOG.debug("Error reading {}", request, e);
100100
}

bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/ReadEntryProcessorV3.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,10 @@ protected ReadResponse getReadResponse() {
215215
}
216216
}
217217
return readEntry(readResponse, entryId, startTimeSw);
218-
} catch (Bookie.NoLedgerException e) {
218+
} catch (Bookie.NoLedgerException | BookieException.LedgerFencedAndDeletedException e) {
219219
if (RequestUtils.isFenceRequest(readRequest)) {
220-
LOG.info("No ledger found reading entry {} when fencing ledger {}", entryId, ledgerId);
220+
LOG.info("No ledger found(or it has been deleted) reading entry {} when fencing ledger {}",
221+
entryId, ledgerId);
221222
} else if (entryId != BookieProtocol.LAST_ADD_CONFIRMED) {
222223
LOG.info("No ledger found while reading entry: {} from ledger: {}", entryId, ledgerId);
223224
} else if (LOG.isDebugEnabled()) {

bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/WriteEntryProcessor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ protected void processPacket() {
9090
} catch (IOException e) {
9191
LOG.error("Error writing {}", request, e);
9292
rc = BookieProtocol.EIO;
93-
} catch (BookieException.LedgerFencedException lfe) {
94-
LOG.warn("Write attempt on fenced ledger {} by client {}", request.getLedgerId(),
93+
} catch (BookieException.LedgerFencedException | BookieException.LedgerFencedAndDeletedException lfe) {
94+
LOG.warn("Write attempt on fenced/deleted ledger {} by client {}", request.getLedgerId(),
9595
requestHandler.ctx().channel().remoteAddress());
9696
rc = BookieProtocol.EFENCED;
9797
} catch (BookieException e) {

bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/WriteEntryProcessorV3.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ public void writeComplete(int rc, long ledgerId, long entryId,
136136
logger.error("Error writing entry:{} to ledger:{}",
137137
entryId, ledgerId, e);
138138
status = StatusCode.EIO;
139-
} catch (BookieException.LedgerFencedException e) {
140-
logger.error("Ledger fenced while writing entry:{} to ledger:{}",
139+
} catch (BookieException.LedgerFencedException | BookieException.LedgerFencedAndDeletedException e) {
140+
logger.error("Ledger fenced/deleted while writing entry:{} to ledger:{}",
141141
entryId, ledgerId, e);
142142
status = StatusCode.EFENCED;
143143
} catch (BookieException e) {

bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/WriteLacProcessorV3.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ public void writeComplete(int rc, long ledgerId, long entryId, BookieId addr, Ob
105105
requestProcessor.bookie.setExplicitLac(Unpooled.wrappedBuffer(lacToAdd),
106106
writeCallback, requestHandler, masterKey);
107107
status = StatusCode.EOK;
108+
} catch (BookieException.LedgerFencedAndDeletedException e) {
109+
logger.error("Error saving lac {} for ledger:{}, which has been deleted",
110+
lac, ledgerId, e);
111+
status = StatusCode.ENOLEDGER;
108112
} catch (IOException e) {
109113
logger.error("Error saving lac {} for ledger:{}",
110114
lac, ledgerId, e);

bookkeeper-server/src/test/java/org/apache/bookkeeper/client/TestFencing.java

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
import static org.junit.Assert.assertTrue;
2525
import static org.junit.Assert.fail;
2626

27+
import io.netty.buffer.ByteBuf;
28+
import io.netty.buffer.UnpooledByteBufAllocator;
29+
import java.util.concurrent.CompletableFuture;
2730
import java.util.concurrent.CountDownLatch;
2831
import java.util.concurrent.CyclicBarrier;
2932
import lombok.extern.slf4j.Slf4j;
@@ -33,10 +36,16 @@
3336
import org.apache.bookkeeper.bookie.SortedLedgerStorage;
3437
import org.apache.bookkeeper.bookie.storage.ldb.SingleDirectoryDbLedgerStorage;
3538
import org.apache.bookkeeper.client.BookKeeper.DigestType;
39+
import org.apache.bookkeeper.client.api.WriteFlag;
3640
import org.apache.bookkeeper.conf.ClientConfiguration;
3741
import org.apache.bookkeeper.net.BookieId;
42+
import org.apache.bookkeeper.proto.BookieClientImpl;
43+
import org.apache.bookkeeper.proto.BookieProtocol;
44+
import org.apache.bookkeeper.proto.BookkeeperInternalCallbacks;
45+
import org.apache.bookkeeper.proto.PerChannelBookieClient;
3846
import org.apache.bookkeeper.test.BookKeeperClusterTestCase;
3947
import org.apache.bookkeeper.test.TestStatsProvider;
48+
import org.apache.bookkeeper.util.ByteBufList;
4049
import org.awaitility.reflect.WhiteboxImpl;
4150
import org.junit.Test;
4251
import org.slf4j.Logger;
@@ -152,6 +161,110 @@ private void triggerGC(Bookie bookie) {
152161
}
153162
}
154163

164+
@Test(timeout = 3000 * 1000)
165+
public void testConcurrentFenceAndDeleteLedger() throws Exception {
166+
LedgerHandle writeLedger;
167+
writeLedger = bkc.createLedger(digestType, "password".getBytes());
168+
169+
String tmp = "BookKeeper is cool!";
170+
long lac = 0;
171+
for (int i = 0; i < 10; i++) {
172+
long entryId = writeLedger.addEntry(tmp.getBytes());
173+
LOG.info("entryId: {}", entryId);
174+
lac = entryId;
175+
}
176+
177+
// Fence and delete.
178+
final BookieId bookieId = writeLedger.getLedgerMetadata().getEnsembleAt(0).get(0);
179+
ClientConfiguration clientConfiguration2 = newClientConfiguration();
180+
clientConfiguration2.setUseV2WireProtocol(true);
181+
ClientConfiguration clientConfiguration3 = newClientConfiguration();
182+
BookKeeperTestClient bkcV2 = new BookKeeperTestClient(clientConfiguration2, new TestStatsProvider());
183+
LedgerHandle writeLedgerV2 = bkcV2.createLedger(digestType, "password".getBytes());
184+
BookKeeperTestClient bkcV3 = new BookKeeperTestClient(clientConfiguration3, new TestStatsProvider());
185+
LedgerHandle writeLedgerV3 = bkcV3.createLedger(digestType, "password".getBytes());
186+
ReadOnlyLedgerHandle readLedgerV2 =
187+
(ReadOnlyLedgerHandle) bkcV2.openLedger(writeLedger.getId(), digestType, "password".getBytes());
188+
ReadOnlyLedgerHandle readLedgerV3 =
189+
(ReadOnlyLedgerHandle) bkcV3.openLedger(writeLedger.getId(), digestType, "password".getBytes());
190+
BookieClientImpl bookieClientV2 = (BookieClientImpl) readLedgerV2.clientCtx.getBookieClient();
191+
BookieClientImpl bookieClientV3 = (BookieClientImpl) readLedgerV3.clientCtx.getBookieClient();
192+
// Trigger opening connection.
193+
CompletableFuture<Integer> obtainV2 = new CompletableFuture<>();
194+
bookieClientV2.lookupClient(bookieId).obtain(
195+
new BookkeeperInternalCallbacks.GenericCallback<PerChannelBookieClient>() {
196+
@Override
197+
public void operationComplete(int rc, PerChannelBookieClient result) {
198+
obtainV2.complete(rc);
199+
}
200+
}, writeLedger.getId());
201+
assertEquals(obtainV2.get().intValue(), BKException.Code.OK);
202+
CompletableFuture<Integer> obtainV3 = new CompletableFuture<>();
203+
bookieClientV3.lookupClient(bookieId).obtain(
204+
new BookkeeperInternalCallbacks.GenericCallback<PerChannelBookieClient>() {
205+
@Override
206+
public void operationComplete(int rc, PerChannelBookieClient result) {
207+
obtainV3.complete(rc);
208+
}
209+
}, writeLedger.getId());
210+
assertEquals(obtainV3.get().intValue(), BKException.Code.OK);
211+
bkcV3.deleteLedger(readLedgerV3.ledgerId);
212+
213+
// Waiting for GC.
214+
for (ServerTester server : servers) {
215+
triggerGC(server.getServer().getBookie());
216+
}
217+
218+
// Verify: read requests with V2 protocol will receive a NoSuchLedgerException.
219+
final byte readEntryFlagFencing = 1;
220+
CompletableFuture<Integer> readResV2 = new CompletableFuture<>();
221+
bookieClientV2.readEntry(bookieId,
222+
writeLedger.getId(), 0, (rc, ledgerId, entryId1, buffer, ctx) -> {
223+
readResV2.complete(rc);
224+
}, null, readEntryFlagFencing, readLedgerV2.ledgerKey);
225+
assertEquals(BKException.Code.NoSuchLedgerExistsException, readResV2.get().intValue());
226+
// Verify: read requests with V3 protocol will receive a NoSuchLedgerException.
227+
CompletableFuture<Integer> readResV3 = new CompletableFuture<>();
228+
bookieClientV3.readEntry(bookieId,
229+
writeLedger.getId(), 0, (rc, ledgerId, entryId1, buffer, ctx) -> {
230+
readResV3.complete(rc);
231+
}, null, readEntryFlagFencing, readLedgerV3.ledgerKey);
232+
assertEquals(BKException.Code.NoSuchLedgerExistsException, readResV3.get().intValue());
233+
// Verify: add requests with V2 protocol will receive a NoSuchLedgerException.
234+
log.info("Try to add the next entry: {}:{}", writeLedger.getId(), lac + 1);
235+
final ByteBuf dataV2 = UnpooledByteBufAllocator.DEFAULT.heapBuffer();
236+
// Combine add request, and rewrite ledgerId of the request.
237+
dataV2.writeByte(1);
238+
final ByteBuf toSendV2 = (ByteBuf) writeLedgerV2.macManager.computeDigestAndPackageForSending(
239+
lac + 1, lac, 1, dataV2, writeLedger.ledgerKey, BookieProtocol.FLAG_NONE);
240+
toSendV2.setLong(28, writeLedger.getId());
241+
CompletableFuture<Integer> addResV2 = new CompletableFuture<>();
242+
bookieClientV2.addEntry(bookieId, writeLedger.getId(), writeLedger.ledgerKey, lac + 1, toSendV2,
243+
(rc, ledgerId, entryId1, addr, ctx) -> {
244+
addResV2.complete(rc);
245+
}, null, BookieProtocol.FLAG_NONE, false, WriteFlag.NONE);
246+
assertEquals(BKException.Code.LedgerFencedException, addResV2.get().intValue());
247+
// Verify: read requests with V3 protocol will receive a NoSuchLedgerException.
248+
final ByteBuf dataV3 = UnpooledByteBufAllocator.DEFAULT.heapBuffer();
249+
dataV3.writeByte(1);
250+
// Combine add request, and rewrite ledgerId of the request.
251+
final ByteBufList toSendV3 = (ByteBufList) writeLedgerV3.macManager.computeDigestAndPackageForSending(
252+
lac + 1, lac, 1, dataV3, writeLedger.ledgerKey, BookieProtocol.FLAG_NONE);
253+
toSendV3.getBuffer(0).setLong(0, writeLedger.getId());
254+
CompletableFuture<Integer> addResV3 = new CompletableFuture<>();
255+
bookieClientV3.addEntry(bookieId, writeLedger.getId(), writeLedger.ledgerKey, lac + 1, toSendV3,
256+
(rc, ledgerId, entryId1, addr, ctx) -> {
257+
addResV3.complete(rc);
258+
}, null, BookieProtocol.FLAG_NONE, false, WriteFlag.NONE);
259+
assertEquals(BKException.Code.LedgerFencedException, addResV3.get().intValue());
260+
261+
// cleanup.
262+
writeLedgerV2.close();
263+
writeLedgerV3.close();
264+
bkcV2.close();
265+
bkcV3.close();
266+
}
267+
155268
private static int threadCount = 0;
156269

157270
class LedgerOpenThread extends Thread {

0 commit comments

Comments
 (0)