1616
1717package com .iexec .core .worker ;
1818
19+ import com .iexec .common .utils .ContextualLockRunner ;
1920import com .iexec .core .configuration .WorkerConfiguration ;
2021import lombok .extern .slf4j .Slf4j ;
2122import org .springframework .stereotype .Service ;
2728
2829import static com .iexec .common .utils .DateTimeUtils .addMinutesToDate ;
2930
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+ */
3038@ Slf4j
3139@ Service
3240public class WorkerService {
3341
3442 private final WorkerRepository workerRepository ;
3543 private final WorkerConfiguration workerConfiguration ;
44+ private final ContextualLockRunner <String > contextualLockRunner ;
3645
3746 public WorkerService (WorkerRepository workerRepository ,
3847 WorkerConfiguration workerConfiguration ) {
3948 this .workerRepository = workerRepository ;
4049 this .workerConfiguration = workerConfiguration ;
50+ this .contextualLockRunner = new ContextualLockRunner <>();
4151 }
4252
53+ // region Read methods
4354 public Optional <Worker > getWorker (String walletAddress ) {
4455 return workerRepository .findByWalletAddress (walletAddress );
4556 }
4657
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-
6358 public boolean isAllowedToJoin (String workerAddress ){
6459 List <String > whitelist = workerConfiguration .getWhitelist ();
6560 // if the whitelist is empty, there is no restriction on the workers
@@ -69,18 +64,6 @@ public boolean isAllowedToJoin(String workerAddress){
6964 return whitelist .contains (workerAddress );
7065 }
7166
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-
8467 public boolean isWorkerAllowedToAskReplicate (String walletAddress ) {
8568 Optional <Date > oDate = getLastReplicateDemand (walletAddress );
8669 if (oDate .isEmpty ()) {
@@ -105,29 +88,6 @@ public Optional<Date> getLastReplicateDemand(String walletAddress) {
10588 return Optional .ofNullable (worker .getLastReplicateDemandDate ());
10689 }
10790
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-
13191 public List <String > getChainTaskIds (String walletAddress ) {
13292 Optional <Worker > optional = workerRepository .findByWalletAddress (walletAddress );
13393 if (optional .isPresent ()) {
@@ -146,28 +106,6 @@ public List<String> getComputingTaskIds(String walletAddress) {
146106 return Collections .emptyList ();
147107 }
148108
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-
171109
172110 // worker is considered lost if it didn't ping for 1 minute
173111 public List <Worker > getLostWorkers () {
@@ -249,4 +187,122 @@ public int getAliveAvailableGpu () {
249187 }
250188 return availableGpus ;
251189 }
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
252308}
0 commit comments