2121import io .netty .util .Recycler ;
2222import io .netty .util .Recycler .Handle ;
2323import java .util .ArrayList ;
24- import java .util .Collections ;
2524import java .util .List ;
2625import java .util .function .Predicate ;
2726import org .apache .bookkeeper .mledger .AsyncCallbacks .ReadEntriesCallback ;
@@ -67,7 +66,12 @@ public static OpReadEntry create(ManagedCursorImpl cursor, Position readPosition
6766 return op ;
6867 }
6968
70- void internalReadEntriesComplete (List <Entry > returnedEntries , Object ctx , Position lastPosition ) {
69+ private void internalReadEntriesComplete (List <Entry > returnedEntries ) {
70+ if (returnedEntries .isEmpty ()) {
71+ log .warn ("[{}] Read no entries unexpectedly" , this );
72+ checkReadCompletion ();
73+ return ;
74+ }
7175 // Filter the returned entries for individual deleted messages
7276 int entriesCount = returnedEntries .size ();
7377 long entriesSize = 0 ;
@@ -76,19 +80,15 @@ void internalReadEntriesComplete(List<Entry> returnedEntries, Object ctx, Positi
7680 }
7781 cursor .updateReadStats (entriesCount , entriesSize );
7882
79- if (entriesCount != 0 ) {
80- lastPosition = returnedEntries .get (entriesCount - 1 ).getPosition ();
81- }
8283 if (log .isDebugEnabled ()) {
8384 log .debug ("[{}][{}] Read entries succeeded batch_size={} cumulative_size={} requested_count={}" ,
8485 cursor .ledger .getName (), cursor .getName (), returnedEntries .size (), entries .size (), count );
8586 }
8687
87- List <Entry > filteredEntries = Collections .emptyList ();
88- if (entriesCount != 0 ) {
89- filteredEntries = cursor .filterReadEntries (returnedEntries );
90- entries .addAll (filteredEntries );
91- }
88+ // Entries might be released after `filterReadEntries`, so retrieve the last position before that
89+ final var lastPosition = returnedEntries .get (entriesCount - 1 ).getPosition ();
90+ final var filteredEntries = cursor .filterReadEntries (returnedEntries );
91+ entries .addAll (filteredEntries );
9292
9393 // if entries have been filtered out then try to skip reading of already deletedMessages in that range
9494 final Position nexReadPosition = entriesCount != filteredEntries .size ()
@@ -99,19 +99,30 @@ void internalReadEntriesComplete(List<Entry> returnedEntries, Object ctx, Positi
9999
100100 @ Override
101101 public void readEntriesComplete (List <Entry > returnedEntries , Object ctx ) {
102- internalReadEntriesComplete (returnedEntries , ctx , null );
102+ try {
103+ internalReadEntriesComplete (returnedEntries );
104+ } catch (Throwable throwable ) {
105+ log .error ("[{}] Fallback to readEntriesFailed for exception in readEntriesComplete" , this , throwable );
106+ readEntriesFailed (ManagedLedgerException .getManagedLedgerException (throwable ), ctx );
107+ }
103108 }
104109
105110 @ Override
106111 public void readEntriesFailed (ManagedLedgerException exception , Object ctx ) {
112+ try {
113+ internalReadEntriesFailed (exception , ctx );
114+ } catch (Throwable throwable ) {
115+ // At least we should complete the callback
116+ fail (ManagedLedgerException .getManagedLedgerException (throwable ), ctx );
117+ }
118+ }
119+
120+ private void internalReadEntriesFailed (ManagedLedgerException exception , Object ctx ) {
107121 cursor .readOperationCompleted ();
108122
109123 if (!entries .isEmpty ()) {
110124 // There were already some entries that were read before, we can return them
111- cursor .ledger .getExecutor ().execute (() -> {
112- callback .readEntriesComplete (entries , ctx );
113- recycle ();
114- });
125+ complete (ctx );
115126 } else if (cursor .getConfig ().isAutoSkipNonRecoverableData ()
116127 && exception instanceof NonRecoverableLedgerException ) {
117128 log .warn ("[{}][{}] read failed from ledger at position:{} : {}" , cursor .ledger .getName (), cursor .getName (),
@@ -129,9 +140,7 @@ public void readEntriesFailed(ManagedLedgerException exception, Object ctx) {
129140 }
130141 // fail callback if it couldn't find next valid ledger
131142 if (nexReadPosition == null ) {
132- callback .readEntriesFailed (exception , ctx );
133- cursor .ledger .mbean .recordReadEntriesError ();
134- recycle ();
143+ fail (exception , ctx );
135144 return ;
136145 }
137146 updateReadPosition (nexReadPosition );
@@ -152,9 +161,7 @@ public void readEntriesFailed(ManagedLedgerException exception, Object ctx) {
152161 }
153162 }
154163
155- callback .readEntriesFailed (exception , ctx );
156- cursor .ledger .mbean .recordReadEntriesError ();
157- recycle ();
164+ fail (exception , ctx );
158165 }
159166 }
160167
@@ -177,12 +184,8 @@ void checkReadCompletion() {
177184 // The reading was already completed, release resources and trigger callback
178185 try {
179186 cursor .readOperationCompleted ();
180-
181187 } finally {
182- cursor .ledger .getExecutor ().execute (() -> {
183- callback .readEntriesComplete (entries , ctx );
184- recycle ();
185- });
188+ complete (ctx );
186189 }
187190 }
188191 }
@@ -217,5 +220,54 @@ public void recycle() {
217220 recyclerHandle .recycle (this );
218221 }
219222
223+ private void complete (Object ctx ) {
224+ cursor .ledger .getExecutor ().execute (() -> {
225+ try {
226+ callback .readEntriesComplete (entries , ctx );
227+ recycle ();
228+ } catch (Throwable throwable ) {
229+ log .error ("[{}] readEntriesComplete failed (last position: {})" , this , lastEntryPosition (), throwable );
230+ }
231+ });
232+ }
233+
234+ private void fail (ManagedLedgerException e , Object ctx ) {
235+ try {
236+ callback .readEntriesFailed (e , ctx );
237+ cursor .ledger .mbean .recordReadEntriesError ();
238+ recycle ();
239+ } catch (Throwable throwable ) {
240+ log .error ("[{}] readEntriesFailed failed (exception: {})" , this , e .getMessage (), throwable );
241+ }
242+ }
243+
244+ @ Override
245+ public String toString () {
246+ final var cursor = this .cursor ;
247+ final var readPosition = this .readPosition ;
248+ final var nextReadPosition = this .nextReadPosition ;
249+ final var entries = this .entries ;
250+ final var maxPosition = this .maxPosition ;
251+ final var count = this .count ;
252+ if (cursor != null ) {
253+ return cursor .ledger .getName () + " " + cursor .getName () + "{ readPosition: "
254+ + (readPosition != null ? readPosition : "(null)" ) + ", nextReadPosition: "
255+ + (nextReadPosition != null ? nextReadPosition : "(null)" ) + ", maxPosition: "
256+ + (maxPosition != null ? maxPosition : "(null)" ) + ", entries count: "
257+ + (entries != null ? entries .size () : "(null)" ) + ", count: " + count + " }" ;
258+ } else {
259+ return "(null)" ;
260+ }
261+ }
262+
263+ private String lastEntryPosition () {
264+ final var entries = this .entries ;
265+ if (entries != null ) {
266+ return entries .isEmpty () ? "(empty)" : entries .get (entries .size () - 1 ).getPosition ().toString ();
267+ } else {
268+ return "(null)" ;
269+ }
270+ }
271+
220272 private static final Logger log = LoggerFactory .getLogger (OpReadEntry .class );
221273}
0 commit comments