16
16
17
17
package com .iexec .core .worker ;
18
18
19
+ import com .iexec .common .utils .ContextualLockRunner ;
19
20
import com .iexec .core .configuration .WorkerConfiguration ;
20
21
import lombok .extern .slf4j .Slf4j ;
21
22
import org .springframework .stereotype .Service ;
27
28
28
29
import static com .iexec .common .utils .DateTimeUtils .addMinutesToDate ;
29
30
31
+ /**
32
+ * Manage {@link Worker} objects.
33
+ * <p>
34
+ * /!\ Private read-and-write methods are not thread-safe.
35
+ * They can sometime lead to race conditions.
36
+ * Please use the public, thread-safe, versions of these methods instead.
37
+ */
30
38
@ Slf4j
31
39
@ Service
32
40
public class WorkerService {
33
41
34
42
private final WorkerRepository workerRepository ;
35
43
private final WorkerConfiguration workerConfiguration ;
44
+ private final ContextualLockRunner <String > contextualLockRunner ;
36
45
37
46
public WorkerService (WorkerRepository workerRepository ,
38
47
WorkerConfiguration workerConfiguration ) {
39
48
this .workerRepository = workerRepository ;
40
49
this .workerConfiguration = workerConfiguration ;
50
+ this .contextualLockRunner = new ContextualLockRunner <>();
41
51
}
42
52
53
+ // region Read methods
43
54
public Optional <Worker > getWorker (String walletAddress ) {
44
55
return workerRepository .findByWalletAddress (walletAddress );
45
56
}
46
57
47
- public Worker addWorker (Worker worker ) {
48
- Optional <Worker > oWorker = workerRepository .findByWalletAddress (worker .getWalletAddress ());
49
-
50
- if (oWorker .isPresent ()) {
51
- Worker existingWorker = oWorker .get ();
52
- log .info ("The worker is already registered [workerId:{}]" , existingWorker .getId ());
53
- worker .setId (existingWorker .getId ());
54
- worker .setParticipatingChainTaskIds (existingWorker .getParticipatingChainTaskIds ());
55
- worker .setComputingChainTaskIds (existingWorker .getComputingChainTaskIds ());
56
- } else {
57
- log .info ("Registering new worker" );
58
- }
59
-
60
- return workerRepository .save (worker );
61
- }
62
-
63
58
public boolean isAllowedToJoin (String workerAddress ){
64
59
List <String > whitelist = workerConfiguration .getWhitelist ();
65
60
// if the whitelist is empty, there is no restriction on the workers
@@ -69,18 +64,6 @@ public boolean isAllowedToJoin(String workerAddress){
69
64
return whitelist .contains (workerAddress );
70
65
}
71
66
72
- public Optional <Worker > updateLastAlive (String walletAddress ) {
73
- Optional <Worker > optional = workerRepository .findByWalletAddress (walletAddress );
74
- if (optional .isPresent ()) {
75
- Worker worker = optional .get ();
76
- worker .setLastAliveDate (new Date ());
77
- workerRepository .save (worker );
78
- return Optional .of (worker );
79
- }
80
-
81
- return Optional .empty ();
82
- }
83
-
84
67
public boolean isWorkerAllowedToAskReplicate (String walletAddress ) {
85
68
Optional <Date > oDate = getLastReplicateDemand (walletAddress );
86
69
if (oDate .isEmpty ()) {
@@ -105,29 +88,6 @@ public Optional<Date> getLastReplicateDemand(String walletAddress) {
105
88
return Optional .ofNullable (worker .getLastReplicateDemandDate ());
106
89
}
107
90
108
- public Optional <Worker > updateLastReplicateDemandDate (String walletAddress ) {
109
- Optional <Worker > optional = workerRepository .findByWalletAddress (walletAddress );
110
- if (optional .isPresent ()) {
111
- Worker worker = optional .get ();
112
- worker .setLastReplicateDemandDate (new Date ());
113
- workerRepository .save (worker );
114
- return Optional .of (worker );
115
- }
116
-
117
- return Optional .empty ();
118
- }
119
-
120
- public Optional <Worker > addChainTaskIdToWorker (String chainTaskId , String walletAddress ) {
121
- Optional <Worker > optional = workerRepository .findByWalletAddress (walletAddress );
122
- if (optional .isPresent ()) {
123
- Worker worker = optional .get ();
124
- worker .addChainTaskId (chainTaskId );
125
- log .info ("Added chainTaskId to worker [chainTaskId:{}, workerName:{}]" , chainTaskId , walletAddress );
126
- return Optional .of (workerRepository .save (worker ));
127
- }
128
- return Optional .empty ();
129
- }
130
-
131
91
public List <String > getChainTaskIds (String walletAddress ) {
132
92
Optional <Worker > optional = workerRepository .findByWalletAddress (walletAddress );
133
93
if (optional .isPresent ()) {
@@ -146,28 +106,6 @@ public List<String> getComputingTaskIds(String walletAddress) {
146
106
return Collections .emptyList ();
147
107
}
148
108
149
- public Optional <Worker > removeChainTaskIdFromWorker (String chainTaskId , String walletAddress ) {
150
- Optional <Worker > optional = workerRepository .findByWalletAddress (walletAddress );
151
- if (optional .isPresent ()) {
152
- Worker worker = optional .get ();
153
- worker .removeChainTaskId (chainTaskId );
154
- log .info ("Removed chainTaskId from worker [chainTaskId:{}, walletAddress:{}]" , chainTaskId , walletAddress );
155
- return Optional .of (workerRepository .save (worker ));
156
- }
157
- return Optional .empty ();
158
- }
159
-
160
- public Optional <Worker > removeComputedChainTaskIdFromWorker (String chainTaskId , String walletAddress ) {
161
- Optional <Worker > optional = workerRepository .findByWalletAddress (walletAddress );
162
- if (optional .isPresent ()) {
163
- Worker worker = optional .get ();
164
- worker .removeComputedChainTaskId (chainTaskId );
165
- log .info ("Removed computed chainTaskId from worker [chainTaskId:{}, walletAddress:{}]" , chainTaskId , walletAddress );
166
- return Optional .of (workerRepository .save (worker ));
167
- }
168
- return Optional .empty ();
169
- }
170
-
171
109
172
110
// worker is considered lost if it didn't ping for 1 minute
173
111
public List <Worker > getLostWorkers () {
@@ -249,4 +187,122 @@ public int getAliveAvailableGpu () {
249
187
}
250
188
return availableGpus ;
251
189
}
190
+ // endregion
191
+
192
+ // region Read-and-write methods
193
+ public Worker addWorker (Worker worker ) {
194
+ return contextualLockRunner .applyWithLock (
195
+ worker .getWalletAddress (),
196
+ address -> addWorkerWithoutThreadSafety (worker )
197
+ );
198
+ }
199
+
200
+ private Worker addWorkerWithoutThreadSafety (Worker worker ) {
201
+ Optional <Worker > oWorker = workerRepository .findByWalletAddress (worker .getWalletAddress ());
202
+
203
+ if (oWorker .isPresent ()) {
204
+ Worker existingWorker = oWorker .get ();
205
+ log .info ("The worker is already registered [workerId:{}]" , existingWorker .getId ());
206
+ worker .setId (existingWorker .getId ());
207
+ worker .setParticipatingChainTaskIds (existingWorker .getParticipatingChainTaskIds ());
208
+ worker .setComputingChainTaskIds (existingWorker .getComputingChainTaskIds ());
209
+ } else {
210
+ log .info ("Registering new worker" );
211
+ }
212
+
213
+ return workerRepository .save (worker );
214
+ }
215
+
216
+ public Optional <Worker > updateLastAlive (String walletAddress ) {
217
+ return contextualLockRunner .applyWithLock (
218
+ walletAddress ,
219
+ this ::updateLastAliveWithoutThreadSafety
220
+ );
221
+ }
222
+
223
+ private Optional <Worker > updateLastAliveWithoutThreadSafety (String walletAddress ) {
224
+ Optional <Worker > optional = workerRepository .findByWalletAddress (walletAddress );
225
+ if (optional .isPresent ()) {
226
+ Worker worker = optional .get ();
227
+ worker .setLastAliveDate (new Date ());
228
+ workerRepository .save (worker );
229
+ return Optional .of (worker );
230
+ }
231
+
232
+ return Optional .empty ();
233
+ }
234
+
235
+ public Optional <Worker > updateLastReplicateDemandDate (String walletAddress ) {
236
+ return contextualLockRunner .applyWithLock (
237
+ walletAddress ,
238
+ this ::updateLastReplicateDemandDateWithoutThreadSafety
239
+ );
240
+ }
241
+
242
+ private Optional <Worker > updateLastReplicateDemandDateWithoutThreadSafety (String walletAddress ) {
243
+ Optional <Worker > optional = workerRepository .findByWalletAddress (walletAddress );
244
+ if (optional .isPresent ()) {
245
+ Worker worker = optional .get ();
246
+ worker .setLastReplicateDemandDate (new Date ());
247
+ workerRepository .save (worker );
248
+ return Optional .of (worker );
249
+ }
250
+
251
+ return Optional .empty ();
252
+ }
253
+
254
+ public Optional <Worker > addChainTaskIdToWorker (String chainTaskId , String walletAddress ) {
255
+ return contextualLockRunner .applyWithLock (
256
+ walletAddress ,
257
+ address -> addChainTaskIdToWorkerWithoutThreadSafety (chainTaskId , address )
258
+ );
259
+ }
260
+
261
+ private Optional <Worker > addChainTaskIdToWorkerWithoutThreadSafety (String chainTaskId , String walletAddress ) {
262
+ Optional <Worker > optional = workerRepository .findByWalletAddress (walletAddress );
263
+ if (optional .isPresent ()) {
264
+ Worker worker = optional .get ();
265
+ worker .addChainTaskId (chainTaskId );
266
+ log .info ("Added chainTaskId to worker [chainTaskId:{}, workerName:{}]" , chainTaskId , walletAddress );
267
+ return Optional .of (workerRepository .save (worker ));
268
+ }
269
+ return Optional .empty ();
270
+ }
271
+
272
+ public Optional <Worker > removeChainTaskIdFromWorker (String chainTaskId , String walletAddress ) {
273
+ return contextualLockRunner .applyWithLock (
274
+ walletAddress ,
275
+ address -> removeChainTaskIdFromWorkerWithoutThreadSafety (chainTaskId , address )
276
+ );
277
+ }
278
+
279
+ private Optional <Worker > removeChainTaskIdFromWorkerWithoutThreadSafety (String chainTaskId , String walletAddress ) {
280
+ Optional <Worker > optional = workerRepository .findByWalletAddress (walletAddress );
281
+ if (optional .isPresent ()) {
282
+ Worker worker = optional .get ();
283
+ worker .removeChainTaskId (chainTaskId );
284
+ log .info ("Removed chainTaskId from worker [chainTaskId:{}, walletAddress:{}]" , chainTaskId , walletAddress );
285
+ return Optional .of (workerRepository .save (worker ));
286
+ }
287
+ return Optional .empty ();
288
+ }
289
+
290
+ public Optional <Worker > removeComputedChainTaskIdFromWorker (String chainTaskId , String walletAddress ) {
291
+ return contextualLockRunner .applyWithLock (
292
+ walletAddress ,
293
+ address -> removeComputedChainTaskIdFromWorkerWithoutThreadSafety (chainTaskId , address )
294
+ );
295
+ }
296
+
297
+ private Optional <Worker > removeComputedChainTaskIdFromWorkerWithoutThreadSafety (String chainTaskId , String walletAddress ) {
298
+ Optional <Worker > optional = workerRepository .findByWalletAddress (walletAddress );
299
+ if (optional .isPresent ()) {
300
+ Worker worker = optional .get ();
301
+ worker .removeComputedChainTaskId (chainTaskId );
302
+ log .info ("Removed computed chainTaskId from worker [chainTaskId:{}, walletAddress:{}]" , chainTaskId , walletAddress );
303
+ return Optional .of (workerRepository .save (worker ));
304
+ }
305
+ return Optional .empty ();
306
+ }
307
+ // endregion
252
308
}
0 commit comments