@@ -59,7 +59,7 @@ public final class BatchContext<PropertiesT> implements Closeable {
5959 private final CollectionHandleDefaults collectionHandleDefaults ;
6060
6161 /**
62- * Internal execution service. It's lifecycle is bound to that of the
62+ * Internal execution service. Its lifecycle is bound to that of the
6363 * BatchContext: it's started when the context is initialized
6464 * and shutdown on {@link #close}.
6565 *
@@ -87,20 +87,19 @@ public final class BatchContext<PropertiesT> implements Closeable {
8787
8888 /**
8989 * Queue publishes insert tasks from the main thread to the "sender".
90- * It has a maximum capacity of {@link #queueSize}.
9190 *
92- * Send {@link TaskHandle#POISON} to gracefully shutdown the "sender"
91+ * Send {@link TaskHandle#POISON} to gracefully shut down the "sender"
9392 * thread. The same queue may be re-used with a different "sender",
9493 * e.g. after {@link #reconnect}, but only when the new thread is known
95- * to have started. Otherwise the thread trying to put an item on
94+ * to have started. Otherwise, the thread trying to put an item on
9695 * the queue will block indefinitely.
9796 */
9897 private final BlockingQueue <TaskHandle > queue ;
9998
10099 /**
101100 * Work-in-progress items.
102101 *
103- * An item is added to the {@link # wip} map after the Sender successfully
102+ * An item is added to the wip map after the Sender successfully
104103 * adds it to the {@link #batch} and is removed once the server reports
105104 * back the result (whether success of failure).
106105 */
@@ -110,14 +109,14 @@ public final class BatchContext<PropertiesT> implements Closeable {
110109 * Current batch.
111110 *
112111 * <p>
113- * An item is added to the {@link # batch} after the Sender pulls it
112+ * An item is added to the batch after the Sender pulls it
114113 * from the queue and remains there until it's Ack'ed.
115114 */
116115 private final Batch batch ;
117116
118117 /**
119118 * State encapsulates state-dependent behavior of the {@link BatchContext}.
120- * Before reading {@link # state} , a thread MUST acquire {@link #lock}.
119+ * Before reading state, a thread MUST acquire {@link #lock}.
121120 */
122121 @ GuardedBy ("lock" )
123122 private State state ;
@@ -137,7 +136,7 @@ public final class BatchContext<PropertiesT> implements Closeable {
137136
138137 /**
139138 * Latch reaches zero once both "send" (client side) and "recv" (server side)
140- * parts of the stream have closed. After a {@link reconnect}, the latch is
139+ * parts of the stream have closed. After a {@link # reconnect}, the latch is
141140 * reset.
142141 */
143142 private volatile CountDownLatch workers ;
@@ -195,37 +194,38 @@ public TaskHandle add(BatchReference reference) throws InterruptedException {
195194 }
196195
197196 void start () {
198- start (AWAIT_STARTED );
199- }
200-
201- void start (State nextState ) {
197+ System .out .println ("RESET COUNTDOWN LATCH to [2]" );
202198 workers = new CountDownLatch (2 );
203199
204200 messages = streamFactory .createStream (new Recv ());
205-
206- // Start the stream and await Started message.
207201 messages .onNext (Message .start (collectionHandleDefaults .consistencyLevel ()));
208- setState (nextState );
209202
210203 // "send" routine must start after the nextState has been set.
204+ setState (AWAIT_STARTED );
211205 send = sendExec .submit (new Send ());
212206 }
213207
214208 /**
215- * Reconnect waits for "send" and "recv" streams to exit
216- * and restarts the process with a new stream.
209+ * Reconnect resets {@link #workers} latch and re-creates the stream.
217210 *
218- * @param reconnecting Reconnecting instance that called reconnect.
211+ * <p>
212+ * Unlike {@link #start} it does not trigger a state transition, and
213+ * {@link Reconnecting} will should continue to handle events until
214+ * the stream is renewed successfully or {@link #maxReconnectRetries}
215+ * is reached.
219216 */
220- void reconnect (Reconnecting reconnecting ) throws InterruptedException , ExecutionException {
221- workers .await ();
222- send .get ();
223- start (reconnecting );
217+ void reconnect () throws InterruptedException , ExecutionException {
218+ System .out .println ("RESET COUNTDOWN LATCH to [1]" );
219+ workers = new CountDownLatch (2 );
220+
221+ messages = streamFactory .createStream (new Recv ());
222+ messages .onNext (Message .start (collectionHandleDefaults .consistencyLevel ()));
224223 }
225224
226225 /**
227226 * Retry a task.
228227 *
228+ * <p>
229229 * BatchContext does not impose any limit on the number of times a task can
230230 * be retried -- it is up to the user to implement an appropriate retry policy.
231231 *
@@ -249,6 +249,7 @@ public void close() throws IOException {
249249 closed = true ;
250250
251251 if (!closedBefore ) {
252+ System .out .println ("CLOSE CONTEXT" );
252253 shutdown ();
253254 }
254255
@@ -268,18 +269,21 @@ public void close() throws IOException {
268269 private void shutdown () {
269270 CompletableFuture .runAsync (() -> {
270271 try {
271- // Poison the queue -- this will signal "send" to drain the remaing
272+ // Poison the queue -- this will signal "send" to drain the remaining
272273 // items in the batch and in the backlog and exit.
273274 //
274275 // If shutdownNow has been called previously and the "send" routine
275- // has been interrupted, this may block indefinitely.
276- // However, shutdownNow ensures that `closing` future is resolved.
276+ // has been interrupted, this would block indefinitely.
277+ // Luckily, shutdownNow resolves the `closing` future as well.
278+ System .out .println ("POISON THE QUEUE" );
277279 queue .put (TaskHandle .POISON );
278280
279281 // Wait for the send to exit before closing our end of the stream.
280- send .get ();
281- messages .onNext (Message .stop ());
282- messages .onCompleted ();
282+ try {
283+ send .get ();
284+ } catch (CancellationException ignored ) {
285+ // Send task can be cancelled due to a reconnect or an internal error.
286+ }
283287
284288 // Wait for both "send" and "recv" to exit.
285289 workers .await ();
@@ -405,6 +409,7 @@ public void run() {
405409 try {
406410 trySend ();
407411 } finally {
412+ System .out .println ("sender countDown" );
408413 workers .countDown ();
409414 }
410415 }
@@ -428,7 +433,7 @@ private void trySend() {
428433 if (task == TaskHandle .POISON ) {
429434 System .out .println ("took POISON" );
430435 drain ();
431- return ;
436+ break ;
432437 }
433438
434439 Data data = task .data ();
@@ -443,6 +448,9 @@ private void trySend() {
443448 onEvent (new Event .ClientError (e ));
444449 return ;
445450 }
451+
452+ messages .onNext (Message .stop ());
453+ messages .onCompleted ();
446454 }
447455
448456 /**
@@ -535,6 +543,7 @@ public void onCompleted() {
535543 try {
536544 onEvent (Event .EOF );
537545 } finally {
546+ System .out .println ("recv countDown (onCompleted)" );
538547 workers .countDown ();
539548 }
540549 }
@@ -545,6 +554,7 @@ public void onError(Throwable t) {
545554 try {
546555 onEvent (Event .StreamHangup .fromThrowable (t ));
547556 } finally {
557+ System .out .println ("recv countDown (onError)" );
548558 workers .countDown ();
549559 }
550560 }
@@ -662,20 +672,20 @@ public void onEvent(Event event) {
662672 }
663673 }
664674
665- private final void onResults (Event .Results results ) {
675+ private void onResults (Event .Results results ) {
666676 results .successful ().forEach (id -> wip .remove (id ).setSuccess ());
667677 results .errors ().forEach ((id , error ) -> wip .remove (id ).setError (error ));
668678 }
669679
670- private final void onBackoff (Event .Backoff backoff ) {
680+ private void onBackoff (Event .Backoff backoff ) {
671681 batch .setMaxSize (backoff .maxSize ());
672682 }
673683
674- private final void onShuttingDown () {
684+ private void onShuttingDown () {
675685 setState (new ServerShuttingDown (this ));
676686 }
677687
678- private final void onStreamClosed (Event event ) {
688+ private void onStreamClosed (Event event ) {
679689 if (event instanceof Event .StreamHangup hangup ) {
680690 hangup .exception ().printStackTrace ();
681691 }
@@ -684,7 +694,7 @@ private final void onStreamClosed(Event event) {
684694 }
685695 }
686696
687- private final void onClientError (Event .ClientError error ) {
697+ private void onClientError (Event .ClientError error ) {
688698 shutdownNow (error .exception ());
689699 }
690700
@@ -696,7 +706,7 @@ public String toString() {
696706
697707 /**
698708 * Oom waits for {@link Event#SHUTTING_DOWN} up to a specified amount of time,
699- * after which it will force stream termiation by imitating server shutdown.
709+ * after which it will force stream termination by imitating server shutdown.
700710 */
701711 private final class Oom extends BaseState {
702712 private final long delaySeconds ;
@@ -719,11 +729,11 @@ private void initiateShutdown() {
719729 // receive an Event.SHUTTING_DOWN, it would cancel this execution of this
720730 // very sequence. Instead, we delegate to our parent BaseState which normally
721731 // handles these events.
722- if (Thread .currentThread ().isInterrupted ()) {
723- super .onEvent (Event .SHUTTING_DOWN );
732+ if (! Thread .currentThread ().isInterrupted ()) {
733+ BatchContext . this .onEvent (Event .SHUTTING_DOWN );
724734 }
725- if (Thread .currentThread ().isInterrupted ()) {
726- super .onEvent (Event .EOF );
735+ if (! Thread .currentThread ().isInterrupted ()) {
736+ BatchContext . this .onEvent (Event .EOF );
727737 }
728738 }
729739
@@ -761,7 +771,7 @@ private final class ServerShuttingDown extends BaseState {
761771
762772 private ServerShuttingDown (State previous ) {
763773 super ("SERVER_SHUTTING_DOWN" );
764- this .canPrepareNext = requireNonNull ( previous , "previous is null" ). getClass () != Oom .class ;
774+ this .canPrepareNext = previous == null || ! Oom .class . isAssignableFrom ( previous . getClass ()) ;
765775 }
766776
767777 @ Override
@@ -776,13 +786,14 @@ public boolean canSend() {
776786
777787 @ Override
778788 public void onEnter (State prev ) {
779- send .cancel (true );
789+ messages .onNext (Message .stop ());
790+ messages .onCompleted ();
780791 }
781792 }
782793
783794 /**
784- * Reconnecting state is entererd either by the server finishing a shutdown
785- * and closing it's end of the stream or an unexpected stream hangup.
795+ * Reconnecting state is entered either by the server finishing a shutdown
796+ * and closing its end of the stream or an unexpected stream hangup.
786797 *
787798 * @see Recv#onCompleted graceful server shutdown
788799 * @see Recv#onError stream hangup
@@ -805,9 +816,9 @@ public void onEnter(State prev) {
805816 return ;
806817 }
807818
808- send .cancel (true );
819+ // send.cancel(true);
809820
810- if (prev .getClass () != ServerShuttingDown . class ) {
821+ if (! ServerShuttingDown . class . isAssignableFrom ( prev .getClass ()) ) {
811822 // This is NOT an orderly shutdown, we're reconnecting after a stream hangup.
812823 // Assume all WIP items have been lost and re-submit everything.
813824 // All items in the batch are contained in WIP, so it is safe to discard the
@@ -836,8 +847,10 @@ public void onEvent(Event event) {
836847 if (retries == maxRetries ) {
837848 onEvent (new Event .ClientError (new IOException ("Server unavailable" )));
838849 } else {
839- reconnectAfter (1 * 2 ^ retries );
850+ reconnectAfter (2 ^ retries );
840851 }
852+ } else {
853+ super .onEvent (event );
841854 }
842855
843856 assert retries <= maxRetries : "maxRetries exceeded" ;
@@ -864,7 +877,7 @@ private void reconnectAfter(long delaySeconds) {
864877
865878 scheduledExec .schedule (() -> {
866879 try {
867- reconnect (this );
880+ reconnect ();
868881 } catch (InterruptedException e ) {
869882 Thread .currentThread ().interrupt ();
870883 } catch (ExecutionException e ) {
@@ -888,7 +901,7 @@ void scheduleReconnect(int reconnectIntervalSeconds) {
888901 }
889902
890903 // We want to count down from the moment we re-opened the stream,
891- // not from the moment we initialited the sequence.
904+ // not from the moment we initialized the sequence.
892905 lock .lock ();
893906 try {
894907 while (state != ACTIVE ) {
0 commit comments