3434import com .google .cloud .pubsublite .proto .SubscriberServiceGrpc .SubscriberServiceStub ;
3535import com .google .common .annotations .VisibleForTesting ;
3636import com .google .common .collect .ImmutableList ;
37+ import com .google .common .util .concurrent .Monitor ;
3738import io .grpc .Status ;
3839import io .grpc .StatusException ;
3940import java .util .Optional ;
@@ -56,11 +57,24 @@ public class SubscriberImpl extends ProxyService
5657 private final TokenCounter tokenCounter = new TokenCounter ();
5758
5859 @ GuardedBy ("monitor.monitor" )
59- private Optional <SettableApiFuture <Offset >> inFlightSeek = Optional .empty ();
60+ private Optional <InFlightSeek > inFlightSeek = Optional .empty ();
61+
62+ @ GuardedBy ("monitor.monitor" )
63+ private boolean internalSeekInFlight = false ;
6064
6165 @ GuardedBy ("monitor.monitor" )
6266 private boolean shutdown = false ;
6367
68+ private static class InFlightSeek {
69+ final SeekRequest seekRequest ;
70+ final SettableApiFuture <Offset > seekFuture ;
71+
72+ InFlightSeek (SeekRequest request , SettableApiFuture <Offset > future ) {
73+ seekRequest = request ;
74+ seekFuture = future ;
75+ }
76+ }
77+
6478 @ VisibleForTesting
6579 SubscriberImpl (
6680 SubscriberServiceStub stub ,
@@ -91,7 +105,7 @@ public SubscriberImpl(
91105 protected void handlePermanentError (StatusException error ) {
92106 try (CloseableMonitor .Hold h = monitor .enter ()) {
93107 shutdown = true ;
94- inFlightSeek .ifPresent (inFlight -> inFlight .setException (error ));
108+ inFlightSeek .ifPresent (inFlight -> inFlight .seekFuture . setException (error ));
95109 inFlightSeek = Optional .empty ();
96110 onPermanentError (error );
97111 }
@@ -106,7 +120,7 @@ protected void stop() {
106120 shutdown = true ;
107121 inFlightSeek .ifPresent (
108122 inFlight ->
109- inFlight .setException (
123+ inFlight .seekFuture . setException (
110124 Status .ABORTED
111125 .withDescription ("Client stopped while seek in flight." )
112126 .asException ()));
@@ -115,13 +129,21 @@ protected void stop() {
115129
116130 @ Override
117131 public ApiFuture <Offset > seek (SeekRequest request ) {
118- try (CloseableMonitor .Hold h = monitor .enter ()) {
132+ try (CloseableMonitor .Hold h =
133+ monitor .enterWhenUninterruptibly (
134+ new Monitor .Guard (monitor .monitor ) {
135+ @ Override
136+ public boolean isSatisfied () {
137+ return !internalSeekInFlight || shutdown ;
138+ }
139+ })) {
119140 checkArgument (
120141 Predicates .isValidSeekRequest (request ), "Sent SeekRequest with no location set." );
121142 checkState (!shutdown , "Seeked after the stream shut down." );
122143 checkState (!inFlightSeek .isPresent (), "Seeked while seek is already in flight." );
123144 SettableApiFuture <Offset > future = SettableApiFuture .create ();
124- inFlightSeek = Optional .of (future );
145+ inFlightSeek = Optional .of (new InFlightSeek (request , future ));
146+ tokenCounter .onClientSeek ();
125147 connection .modifyConnection (
126148 connectedSubscriber ->
127149 connectedSubscriber .ifPresent (subscriber -> subscriber .seek (request )));
@@ -164,13 +186,17 @@ public void triggerReinitialize() {
164186 connectedSubscriber -> {
165187 checkArgument (monitor .monitor .isOccupiedByCurrentThread ());
166188 checkArgument (connectedSubscriber .isPresent ());
167- nextOffsetTracker
168- .requestForRestart ()
169- .ifPresent (
170- request -> {
171- inFlightSeek = Optional .of (SettableApiFuture .create ());
172- connectedSubscriber .get ().seek (request );
173- });
189+ if (inFlightSeek .isPresent ()) {
190+ connectedSubscriber .get ().seek (inFlightSeek .get ().seekRequest );
191+ } else {
192+ nextOffsetTracker
193+ .requestForRestart ()
194+ .ifPresent (
195+ request -> {
196+ internalSeekInFlight = true ;
197+ connectedSubscriber .get ().seek (request );
198+ });
199+ }
174200 tokenCounter
175201 .requestForRestart ()
176202 .ifPresent (request -> connectedSubscriber .get ().allowFlow (request ));
@@ -212,9 +238,13 @@ private Status onSeekResponse(Offset seekOffset) {
212238 if (shutdown ) {
213239 return Status .OK ;
214240 }
241+ if (internalSeekInFlight ) {
242+ internalSeekInFlight = false ;
243+ return Status .OK ;
244+ }
215245 checkState (inFlightSeek .isPresent (), "No in flight seek, but received a seek response." );
216246 nextOffsetTracker .onClientSeek (seekOffset );
217- inFlightSeek .get ().set (seekOffset );
247+ inFlightSeek .get ().seekFuture . set (seekOffset );
218248 inFlightSeek = Optional .empty ();
219249 return Status .OK ;
220250 } catch (StatusException e ) {
0 commit comments