@@ -6,11 +6,13 @@ import {
6
6
Subject ,
7
7
catchError ,
8
8
concatMap ,
9
+ delay ,
9
10
from ,
10
11
map ,
11
12
mergeMap ,
12
13
of ,
13
14
reduce ,
15
+ retry ,
14
16
tap ,
15
17
throwError ,
16
18
} from 'rxjs' ;
@@ -102,38 +104,55 @@ export class Operator extends ADCSDK.backend.BackendEventSource {
102
104
) ;
103
105
}
104
106
107
+ private deleteUpstreamWithRetry ( upstreamId : string ) {
108
+ // Delete upstream with retry on race condition
109
+ return from (
110
+ this . client . request ( {
111
+ url : `/apisix/admin/upstreams/${ upstreamId } ` ,
112
+ method : 'DELETE' ,
113
+ } ) ,
114
+ ) . pipe (
115
+ retry ( {
116
+ count : 3 ,
117
+ delay : ( error : Error | AxiosError , retryCount : number ) => {
118
+ // Only retry if upstream deletion fails due to "still using" race condition
119
+ if (
120
+ axios . isAxiosError ( error ) &&
121
+ error . response ?. data ?. error_msg ?. includes ( 'is still using it' )
122
+ ) {
123
+ // Exponential backoff: 100ms, 200ms, 400ms
124
+ const delayMs = 100 * Math . pow ( 2 , retryCount - 1 ) ;
125
+ return of ( null ) . pipe ( delay ( delayMs ) ) ;
126
+ }
127
+ // Don't retry other errors
128
+ return throwError ( ( ) => error ) ;
129
+ } ,
130
+ } ) ,
131
+ ) ;
132
+ }
133
+
105
134
private deleteServiceWithUpstream ( event : ADCSDK . Event , servicePath : string ) {
106
- // Delete service first, then upstream
135
+ // Delete service first, then upstream with retry
107
136
return from (
108
137
this . client . request ( {
109
138
url : servicePath ,
110
139
method : 'DELETE' ,
111
140
} ) ,
112
141
) . pipe (
113
- concatMap ( ( ) =>
114
- this . client . request ( {
115
- url : `/apisix/admin/upstreams/${ event . resourceId } ` ,
116
- method : 'DELETE' ,
117
- } ) ,
118
- ) ,
142
+ concatMap ( ( ) => this . deleteUpstreamWithRetry ( event . resourceId ) ) ,
119
143
) ;
120
144
}
121
145
122
146
private deleteUpstreamThenUpdateService ( event : ADCSDK . Event , data : typing . Service , servicePath : string ) {
123
- // Update service first (remove upstream reference), then delete upstream
147
+ // Update service first (remove upstream reference), then delete upstream with retry
124
148
return from (
125
149
this . client . request ( {
126
150
url : servicePath ,
127
151
method : 'PUT' ,
128
152
data,
129
153
} ) ,
130
154
) . pipe (
131
- concatMap ( ( ) =>
132
- this . client . request ( {
133
- url : `/apisix/admin/upstreams/${ event . resourceId } ` ,
134
- method : 'DELETE' ,
135
- } ) ,
136
- ) ,
155
+ concatMap ( ( ) => this . deleteUpstreamWithRetry ( event . resourceId ) ) ,
137
156
) ;
138
157
}
139
158
0 commit comments