2323import org .apache .iotdb .common .rpc .thrift .TSStatus ;
2424import org .apache .iotdb .commons .client .ThriftClient ;
2525import org .apache .iotdb .commons .client .async .AsyncPipeDataTransferServiceClient ;
26+ import org .apache .iotdb .commons .exception .pipe .PipeRuntimeSinkNonReportTimeConfigurableException ;
2627import org .apache .iotdb .commons .pipe .agent .task .progress .CommitterKey ;
2728import org .apache .iotdb .commons .pipe .config .PipeConfig ;
2829import org .apache .iotdb .commons .pipe .event .EnrichedEvent ;
8586import java .util .concurrent .LinkedBlockingQueue ;
8687import java .util .concurrent .atomic .AtomicBoolean ;
8788import java .util .concurrent .atomic .AtomicInteger ;
88- import java .util .concurrent .atomic .AtomicLong ;
8989
9090import static org .apache .iotdb .commons .pipe .config .constant .PipeSinkConstant .CONNECTOR_ENABLE_SEND_TSFILE_LIMIT ;
9191import static org .apache .iotdb .commons .pipe .config .constant .PipeSinkConstant .CONNECTOR_ENABLE_SEND_TSFILE_LIMIT_DEFAULT_VALUE ;
@@ -539,6 +539,8 @@ private void logOnClientException(
539539 * @see PipeConnector#transfer(TsFileInsertionEvent) for more details.
540540 */
541541 private void transferQueuedEventsIfNecessary (final boolean forced ) {
542+ throwIfReceiverProbeIsDelayed ();
543+
542544 if ((retryEventQueue .isEmpty () && retryTsFileQueue .isEmpty ())
543545 || (!forced
544546 && retryEventQueueEventCounter .getTabletInsertionEventCount ()
@@ -600,6 +602,8 @@ private void transferQueuedEventsIfNecessary(final boolean forced) {
600602 }
601603 }
602604
605+ throwIfReceiverProbeIsDelayed ();
606+
603607 // Stop retrying if the execution time exceeds the threshold for better realtime performance
604608 if (System .currentTimeMillis () - retryStartTime
605609 > PipeConfig .getInstance ().getPipeAsyncSinkMaxRetryExecutionTimeMsPerCall ()) {
@@ -747,21 +751,58 @@ public void waitIfReceiverTemporarilyUnavailable(final TEndPoint endPoint) {
747751 return ;
748752 }
749753
750- while (!isClosed .get ()) {
751- final long waitTimeInMs = backoff .getRemainingWaitTimeInMs ();
752- if (waitTimeInMs <= 0 ) {
753- return ;
754+ while (!isClosed .get () && backoff .isActive ()) {
755+ if (backoff .isRetryMaxDurationExceeded ()) {
756+ final long probeDelayInMs = backoff .tryAcquireProbeAndGetDelayInMs ();
757+ if (probeDelayInMs <= 0 ) {
758+ return ;
759+ }
760+ throw createReceiverProbeDelayException (endPointKey , backoff );
754761 }
755762
756- try {
757- Thread .sleep (waitTimeInMs );
758- } catch (final InterruptedException e ) {
759- Thread .currentThread ().interrupt ();
760- return ;
763+ final long retryTimeInMs = backoff .reserveNextRetryTimeInMs ();
764+ while (!isClosed .get () && backoff .isActive ()) {
765+ if (backoff .isRetryMaxDurationExceeded ()) {
766+ break ;
767+ }
768+
769+ final long waitTimeInMs = retryTimeInMs - System .currentTimeMillis ();
770+ if (waitTimeInMs <= 0 ) {
771+ return ;
772+ }
773+
774+ try {
775+ Thread .sleep (Math .min (waitTimeInMs , 1000L ));
776+ } catch (final InterruptedException e ) {
777+ Thread .currentThread ().interrupt ();
778+ return ;
779+ }
761780 }
762781 }
763782 }
764783
784+ private void throwIfReceiverProbeIsDelayed () {
785+ for (final Map .Entry <String , ReceiverTemporaryUnavailableBackoff > entry :
786+ receiverBackoffMap .entrySet ()) {
787+ final long probeDelayInMs = entry .getValue ().getRemainingProbeDelayInMs ();
788+ if (probeDelayInMs <= 0 ) {
789+ continue ;
790+ }
791+
792+ throw createReceiverProbeDelayException (entry .getKey (), entry .getValue ());
793+ }
794+ }
795+
796+ private static PipeRuntimeSinkNonReportTimeConfigurableException
797+ createReceiverProbeDelayException (
798+ final String endPointKey , final ReceiverTemporaryUnavailableBackoff backoff ) {
799+ return new PipeRuntimeSinkNonReportTimeConfigurableException (
800+ String .format (
801+ "Receiver %s remained temporarily unavailable for more than %d ms, pause regular retries and probe every %d ms." ,
802+ endPointKey , backoff .getRetryMaxDurationInMs (), backoff .getRetryProbeIntervalInMs ()),
803+ Long .MAX_VALUE );
804+ }
805+
765806 public void recordReceiverStatus (final TEndPoint endPoint , final TSStatus status ) {
766807 final String endPointKey = format (endPoint );
767808 if (Objects .isNull (endPointKey ) || Objects .isNull (status )) {
@@ -781,10 +822,15 @@ public void recordReceiverStatus(final TEndPoint endPoint, final TSStatus status
781822 status );
782823 }
783824 } else if (isSuccess (status )) {
784- final ReceiverTemporaryUnavailableBackoff backoff = receiverBackoffMap .get (endPointKey );
785- if (Objects .nonNull (backoff ) && backoff .getRemainingWaitTimeInMs () <= 0 ) {
786- receiverBackoffMap .remove (endPointKey , backoff );
787- }
825+ receiverBackoffMap .computeIfPresent (
826+ endPointKey ,
827+ (key , backoff ) -> {
828+ if (!backoff .shouldResetOnSuccess ()) {
829+ return backoff ;
830+ }
831+ backoff .markAvailable ();
832+ return null ;
833+ });
788834 }
789835 }
790836
@@ -994,23 +1040,90 @@ private static class ReceiverTemporaryUnavailableBackoff {
9941040
9951041 private final long maxBackoffTimeInMs =
9961042 Math .max (0 , PipeConfig .getInstance ().getPipeSinkSubtaskSleepIntervalMaxMs ());
997- private final AtomicLong currentBackoffTimeInMs =
998- new AtomicLong (
999- Math .min (
1000- Math .max (1 , PipeConfig .getInstance ().getPipeSinkSubtaskSleepIntervalInitMs ()),
1001- maxBackoffTimeInMs ));
1002- private final AtomicLong nextAvailableTimeInMs = new AtomicLong (0 );
1003-
1004- private long markTemporarilyUnavailable () {
1005- final long backoffTimeInMs = currentBackoffTimeInMs .get ();
1006- nextAvailableTimeInMs .updateAndGet (
1007- current -> Math .max (current , System .currentTimeMillis () + backoffTimeInMs ));
1008- currentBackoffTimeInMs .updateAndGet (this ::getNextBackoffTimeInMs );
1043+ private final long initialBackoffTimeInMs =
1044+ Math .min (
1045+ Math .max (1 , PipeConfig .getInstance ().getPipeSinkSubtaskSleepIntervalInitMs ()),
1046+ maxBackoffTimeInMs );
1047+ private final long retryMaxDurationInMs =
1048+ PipeConfig .getInstance ().getPipeAsyncSinkRetryMaxDurationMs ();
1049+ private final long retryProbeIntervalInMs =
1050+ Math .max (1 , PipeConfig .getInstance ().getPipeAsyncSinkRetryProbeIntervalMs ());
1051+
1052+ private boolean active = false ;
1053+ private long firstUnavailableTimeInMs = 0 ;
1054+ private long currentBackoffTimeInMs = initialBackoffTimeInMs ;
1055+ private long failureBackoffUntilInMs = 0 ;
1056+ private long nextReservedRetryTimeInMs = 0 ;
1057+ private long nextProbeTimeInMs = 0 ;
1058+
1059+ private synchronized long markTemporarilyUnavailable () {
1060+ final long currentTimeInMs = System .currentTimeMillis ();
1061+ if (!active ) {
1062+ active = true ;
1063+ firstUnavailableTimeInMs = currentTimeInMs ;
1064+ currentBackoffTimeInMs = initialBackoffTimeInMs ;
1065+ failureBackoffUntilInMs = 0 ;
1066+ nextReservedRetryTimeInMs = 0 ;
1067+ nextProbeTimeInMs = 0 ;
1068+ }
1069+
1070+ final long backoffTimeInMs = currentBackoffTimeInMs ;
1071+ failureBackoffUntilInMs =
1072+ Math .max (failureBackoffUntilInMs , safeAdd (currentTimeInMs , backoffTimeInMs ));
1073+ nextReservedRetryTimeInMs = Math .max (nextReservedRetryTimeInMs , failureBackoffUntilInMs );
1074+ currentBackoffTimeInMs = getNextBackoffTimeInMs (currentBackoffTimeInMs );
10091075 return backoffTimeInMs ;
10101076 }
10111077
1012- private long getRemainingWaitTimeInMs () {
1013- return nextAvailableTimeInMs .get () - System .currentTimeMillis ();
1078+ private synchronized boolean isActive () {
1079+ return active ;
1080+ }
1081+
1082+ private synchronized boolean isRetryMaxDurationExceeded () {
1083+ return active
1084+ && retryMaxDurationInMs >= 0
1085+ && System .currentTimeMillis () - firstUnavailableTimeInMs >= retryMaxDurationInMs ;
1086+ }
1087+
1088+ private synchronized long reserveNextRetryTimeInMs () {
1089+ final long currentTimeInMs = System .currentTimeMillis ();
1090+ final long retryTimeInMs =
1091+ Math .max (currentTimeInMs , Math .max (failureBackoffUntilInMs , nextReservedRetryTimeInMs ));
1092+ nextReservedRetryTimeInMs = safeAdd (retryTimeInMs , currentBackoffTimeInMs );
1093+ return retryTimeInMs ;
1094+ }
1095+
1096+ private synchronized long tryAcquireProbeAndGetDelayInMs () {
1097+ final long currentTimeInMs = System .currentTimeMillis ();
1098+ if (currentTimeInMs >= nextProbeTimeInMs ) {
1099+ nextProbeTimeInMs = safeAdd (currentTimeInMs , retryProbeIntervalInMs );
1100+ return 0 ;
1101+ }
1102+ return nextProbeTimeInMs - currentTimeInMs ;
1103+ }
1104+
1105+ private synchronized long getRemainingProbeDelayInMs () {
1106+ return isRetryMaxDurationExceeded ()
1107+ ? Math .max (0 , nextProbeTimeInMs - System .currentTimeMillis ())
1108+ : 0 ;
1109+ }
1110+
1111+ private synchronized boolean shouldResetOnSuccess () {
1112+ return active
1113+ && (isRetryMaxDurationExceeded ()
1114+ || failureBackoffUntilInMs - System .currentTimeMillis () <= 0 );
1115+ }
1116+
1117+ private synchronized void markAvailable () {
1118+ active = false ;
1119+ }
1120+
1121+ private long getRetryMaxDurationInMs () {
1122+ return retryMaxDurationInMs ;
1123+ }
1124+
1125+ private long getRetryProbeIntervalInMs () {
1126+ return retryProbeIntervalInMs ;
10141127 }
10151128
10161129 private long getNextBackoffTimeInMs (final long currentBackoffTimeInMs ) {
@@ -1021,5 +1134,9 @@ private long getNextBackoffTimeInMs(final long currentBackoffTimeInMs) {
10211134 ? maxBackoffTimeInMs
10221135 : currentBackoffTimeInMs << 1 ;
10231136 }
1137+
1138+ private static long safeAdd (final long left , final long right ) {
1139+ return left >= Long .MAX_VALUE - right ? Long .MAX_VALUE : left + right ;
1140+ }
10241141 }
10251142}
0 commit comments