|
69 | 69 | * |
70 | 70 | * <h2>State</h2> |
71 | 71 | * |
| 72 | + * BatchContext organized client-side work using the |
| 73 | + * <a href= "https://refactoring.guru/design-patterns/state">State</a> |
| 74 | + * pattern. These states are defined: |
| 75 | + * |
| 76 | + * <ul> |
| 77 | + * <li>{@code null} -- context hasn't been {@link #start}ed yet. The context |
| 78 | + * SHOULD NOT be used in this state, as it will likely result in an NPE. |
| 79 | + * <li>AwaitStarted -- client's opened the stream, sent Start, |
| 80 | + * and is now awaiting for the server to respond with Started. |
| 81 | + * <li>Active -- the server is ready to accept the next Data message. |
| 82 | + * <li>InFlight -- the latest batch has been sent, awaiting Acks. |
| 83 | + * <li>OOM -- server has OOM'ed and will not accept any more data. |
| 84 | + * <li>ServerShuttingDown -- server's begun a graceful shutdown. |
| 85 | + * <li>Reconnecting -- server's closed it's half of the stream; the client |
| 86 | + * will try to reconnect to another instance up to {@link #maxReconnectRetries} |
| 87 | + * times. |
| 88 | + * </ul> |
| 89 | + * |
72 | 90 | * <h2>Cancellation policy</h2> |
| 91 | + * BatchContext does not rely on timing heuristics advance its state. |
| 92 | + * Threads coordinate via {@link #stateChanged} conditional variable |
| 93 | + * and interrupts, when appropriate. |
| 94 | + * |
| 95 | + * <h3>Graceful shutdown</h3> |
| 96 | + * When {@link #close()} is called, the context will stop accepting |
| 97 | + * new items and start draining the remaining items in the {@link #queue} |
| 98 | + * and {@link #batch} backlog. The client will then continue processing |
| 99 | + * server-side events until stream's EOF. By the time context is closed |
| 100 | + * all submitted tasks are expected to be completed successfully or otherwise. |
| 101 | + * |
| 102 | + * <br> |
| 103 | + * N.B.: This may take an arbitrarily long amount time, as the client will |
| 104 | + * continue to re-connect to other instances and re-submit WIP tasks in |
| 105 | + * case the current stream is hung up or the server shuts down prematurely. |
| 106 | + * |
| 107 | + * <h3>Abrupt termination</h3> |
| 108 | + * In the event of an internal client error (e.g. in the "sender" or "recv" |
| 109 | + * threads), the client's half of the stream is closed immediately, and the |
| 110 | + * "sender" processed is cancelled. A subsequent call to {@link #close()} will |
| 111 | + * re-throw the causing exception as {@link IOException}. The stream can be |
| 112 | + * terminated at any time, including during a graceful shutdown. |
| 113 | + * In case the context if terminated <i>before</i> a graceful shutdown begins, |
| 114 | + * the parent thread is also interrupted to prevent {@link #add()} from blocking |
| 115 | + * indefinitely, "sender" will not be there to pop items from the task queue). |
| 116 | + * |
| 117 | + * <p> |
| 118 | + * To prevent data loss, re-submit all incomplete tasks |
| 119 | + * to the next batch context. |
73 | 120 | * |
74 | 121 | * @param <PropertiesT> the shape of properties for inserted objects. |
75 | 122 | * |
76 | 123 | * @see StreamObserver |
| 124 | + * @see State |
| 125 | + * @see shutdownNow |
| 126 | + * @see TaskHandle#result() |
| 127 | + * |
| 128 | + * @author Dyma Solovei |
77 | 129 | */ |
78 | 130 | public final class BatchContext<PropertiesT> implements Closeable { |
79 | 131 | private final int maxReconnectRetries; |
@@ -218,6 +270,10 @@ public TaskHandle add(BatchReference reference) throws InterruptedException { |
218 | 270 | } |
219 | 271 |
|
220 | 272 | void start() { |
| 273 | + if (closed) { |
| 274 | + throw new IllegalStateException("context is closed"); |
| 275 | + } |
| 276 | + |
221 | 277 | workers = new CountDownLatch(2); |
222 | 278 |
|
223 | 279 | messages = streamFactory.createStream(new Recv()); |
@@ -415,7 +471,7 @@ private void onEvent(Event event) { |
415 | 471 |
|
416 | 472 | private TaskHandle add(final TaskHandle taskHandle) throws InterruptedException { |
417 | 473 | if (closed) { |
418 | | - throw new IllegalStateException("BatchContext is closed"); |
| 474 | + throw new IllegalStateException("context is closed"); |
419 | 475 | } |
420 | 476 |
|
421 | 477 | TaskHandle existing = wip.get(taskHandle.id()); |
|
0 commit comments