codelipenghui commented on code in PR #4901:
URL: https://github.com/apache/bookkeeper/pull/4901#discussion_r4333904153


##########
bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/SortedLedgerStorage.java:
##########
@@ -240,7 +240,75 @@ public ByteBuf getEntry(long ledgerId, long entryId) 
throws IOException, BookieE
 
     @Override
     public long getLastAddConfirmed(long ledgerId) throws IOException {
-        return interleavedLedgerStorage.getLastAddConfirmed(ledgerId);
+        LedgerCache ledgerCache = interleavedLedgerStorage.getLedgerCache();
+        Long cachedLac;
+        try {
+            cachedLac = ledgerCache.getLastAddConfirmed(ledgerId);
+        } catch (Bookie.NoLedgerException e) {
+            if (!interleavedLedgerStorage.ledgerExists(ledgerId)) {
+                throw e;
+            }
+            cachedLac = null;

Review Comment:
   Simpler: don't catch `NoLedgerException` here, and drop all six 
`ledgerExists` checks:
   
   ```java
   LedgerCache ledgerCache = interleavedLedgerStorage.getLedgerCache();
   // Throws NoLedgerException for unknown or deleted ledgers. An evicted 
FileInfo
   // reloads from its index file without a LAC, so this returns null instead.
   Long cachedLac = ledgerCache.getLastAddConfirmed(ledgerId);
   if (cachedLac != null) {
       return cachedLac;
   }
   // memtable step and index fallback as they are, minus the ledgerExists 
checks
   ```
   
   That keeps the behavior you want:
   - **#4900 (evicted, nothing flushed):** eviction closes the FileInfo with 
`close(true)`, which writes a header-only index file. The reload finds that 
file and gets a FileInfo without a LAC, so this lookup returns `null` instead 
of throwing. The catch is never reached on this path.
   - **Deleted or unknown ledger:** the lookup throws `NoLedgerException`, 
because the index file is gone or the FileInfo is marked deleted. A V3 LAC read 
then gets ENOLEDGER, as on master.
   - The remaining checks only cover a deletion racing this call, which no 
check can close.
   
   The only other case I found where this lookup throws `NoLedgerException` for 
a live ledger is a failed index relocation, which leaves the ledger's index 
broken anyway.
   
   Locally on 5c77e65 (same PR files), this version passed your real-storage 
tests, including `testGetLastAddConfirmedDoesNotReadDeletedLedgerFromMemTable`, 
plus an end-to-end V3 read of an evicted, unflushed ledger and a deleted-ledger 
check with the FileInfo evicted first. Only two mock tests failed:
   - `testGetLastAddConfirmedRejectsCachedValueForDeletedLedger` mocks a cached 
LAC for a deleted ledger, which real storage doesn't produce. It can go.
   - `testGetLastAddConfirmedFallsBackToMemTableWhenCacheMisses` models 
eviction as the cache throwing. A real eviction returns `null`, so 
`thenReturn(null)` matches it.
   
   The `ledgerExists` stubs would then be unused. Sorry for the churn: this 
catch came from my earlier snippet, and it wasn't needed.



##########
bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/SortedLedgerStorage.java:
##########
@@ -240,7 +240,75 @@ public ByteBuf getEntry(long ledgerId, long entryId) 
throws IOException, BookieE
 
     @Override
     public long getLastAddConfirmed(long ledgerId) throws IOException {
-        return interleavedLedgerStorage.getLastAddConfirmed(ledgerId);
+        LedgerCache ledgerCache = interleavedLedgerStorage.getLedgerCache();
+        Long cachedLac;
+        try {
+            cachedLac = ledgerCache.getLastAddConfirmed(ledgerId);
+        } catch (Bookie.NoLedgerException e) {
+            if (!interleavedLedgerStorage.ledgerExists(ledgerId)) {
+                throw e;
+            }
+            cachedLac = null;
+        }
+        if (cachedLac != null) {
+            if (!interleavedLedgerStorage.ledgerExists(ledgerId)) {
+                throw new Bookie.NoLedgerException(ledgerId);
+            }

Review Comment:
   This adds `ledgerExists()` to every cached LAC read, which is the path every 
successful V3 entry read takes (`ReadEntryProcessorV3` reads the LAC after each 
entry). It ends in `SnapshotMap.containsKey`, which takes a read lock shared by 
the whole storage and does three map lookups.
   
   In a local micro-benchmark (1000 ledgers, LAC cached), 8-thread throughput 
on this path dropped from ~6.2 to ~3.75 M calls/s, and single-thread cost went 
from ~34 to ~40 ns. A tight loop overstates the end-to-end effect, but this is 
the path f1aef21 had just brought back to master's cost.
   
   A cached LAC for a deleted ledger only exists while `deleteLedger` is 
running. `removeLedger` deletes the index file and invalidates both file-info 
caches, and any reload after that throws `NoLedgerException`. The window 
between `activeLedgers.remove` and the FileInfo deletion exists on master too, 
and a check here can't close it, since the deletion can start right after the 
check.
   
   Could this check be dropped? The comment above suggests a version without 
any of these checks.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to