Skip to content

Commit 5b637e2

Browse files
committed
Fix procedure lock wait scheduling
1 parent a725ded commit 5b637e2

6 files changed

Lines changed: 98 additions & 25 deletions

File tree

iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/Procedure.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,16 @@ protected void releaseLock(Env env) {
235235
// no op
236236
}
237237

238+
/**
239+
* Called after an execution attempt returns {@link ProcedureLockState#LOCK_EVENT_WAIT}. Override
240+
* it to put the procedure into the corresponding lock wait queue.
241+
*
242+
* @param env env
243+
*/
244+
protected void waitForLock(Env env) {
245+
// no op
246+
}
247+
238248
/**
239249
* Used to keep procedure lock even when the procedure is yielded or suspended.
240250
*

iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/ProcedureExecutor.java

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import org.apache.iotdb.commons.utils.RetryUtils;
2424
import org.apache.iotdb.commons.utils.TestOnly;
2525
import org.apache.iotdb.confignode.i18n.ProcedureMessages;
26-
import org.apache.iotdb.confignode.procedure.env.ConfigNodeProcedureEnv;
2726
import org.apache.iotdb.confignode.procedure.exception.ProcedureException;
2827
import org.apache.iotdb.confignode.procedure.scheduler.ProcedureScheduler;
2928
import org.apache.iotdb.confignode.procedure.scheduler.SimpleProcedureScheduler;
@@ -42,7 +41,6 @@
4241
import java.util.Deque;
4342
import java.util.HashSet;
4443
import java.util.List;
45-
import java.util.Objects;
4644
import java.util.Set;
4745
import java.util.concurrent.ConcurrentHashMap;
4846
import java.util.concurrent.CopyOnWriteArrayList;
@@ -86,6 +84,16 @@ public class ProcedureExecutor<Env> {
8684
private final Env environment;
8785
private final IProcedureStore<Env> store;
8886

87+
private static final class LockStateResult<Env> {
88+
private final ProcedureLockState lockState;
89+
private final Procedure<Env> procedure;
90+
91+
private LockStateResult(ProcedureLockState lockState, Procedure<Env> procedure) {
92+
this.lockState = lockState;
93+
this.procedure = procedure;
94+
}
95+
}
96+
8997
public ProcedureExecutor(
9098
final Env environment, final IProcedureStore<Env> store, final ProcedureScheduler scheduler) {
9199
this.environment = environment;
@@ -329,33 +337,39 @@ private void executeProcedure(Procedure<Env> proc) {
329337
return;
330338
}
331339
ProcedureLockState lockState = null;
340+
Procedure<Env> lockEventWaitProcedure = null;
332341
try {
333342
do {
334343
if (!rootProcStack.acquire()) {
335344
if (rootProcStack.setRollback()) {
336-
lockState = executeRootStackRollback(rootProcId, rootProcStack);
345+
LockStateResult<Env> lockStateResult =
346+
executeRootStackRollback(rootProcId, rootProcStack);
347+
lockState = lockStateResult.lockState;
337348
switch (lockState) {
338349
case LOCK_ACQUIRED:
339350
break;
340351
case LOCK_EVENT_WAIT:
341-
LOG.info(ProcedureMessages.LOCK_EVENT_WAIT_ROLLBACK, proc);
352+
LOG.info(ProcedureMessages.LOCK_EVENT_WAIT_ROLLBACK, lockStateResult.procedure);
342353
rootProcStack.unsetRollback();
354+
lockEventWaitProcedure = lockStateResult.procedure;
343355
break;
344356
case LOCK_YIELD_WAIT:
345357
rootProcStack.unsetRollback();
346-
scheduler.yield(proc);
358+
scheduler.yield(lockStateResult.procedure);
347359
break;
348360
default:
349361
throw new UnsupportedOperationException();
350362
}
351363
} else {
352364
if (!proc.wasExecuted()) {
353-
switch (executeRollback(proc)) {
365+
lockState = executeRollback(proc);
366+
switch (lockState) {
354367
case LOCK_ACQUIRED:
355368
break;
356369
case LOCK_EVENT_WAIT:
357370
LOG.info(
358371
ProcedureMessages.LOCK_EVENT_WAIT_CAN_T_ROLLBACK_CHILD_RUNNING_FOR, proc);
372+
lockEventWaitProcedure = proc;
359373
break;
360374
case LOCK_YIELD_WAIT:
361375
scheduler.yield(proc);
@@ -375,6 +389,9 @@ private void executeProcedure(Procedure<Env> proc) {
375389
case LOCK_YIELD_WAIT:
376390
case LOCK_EVENT_WAIT:
377391
LOG.info(ProcedureMessages.LOCKSTATE_IS, proc, lockState);
392+
if (lockState == ProcedureLockState.LOCK_EVENT_WAIT) {
393+
lockEventWaitProcedure = proc;
394+
}
378395
break;
379396
default:
380397
throw new UnsupportedOperationException();
@@ -397,9 +414,9 @@ private void executeProcedure(Procedure<Env> proc) {
397414
} finally {
398415
// Only after procedure has completed execution can it be allowed to be rescheduled to prevent
399416
// data races
400-
if (Objects.equals(lockState, ProcedureLockState.LOCK_EVENT_WAIT)) {
401-
LOG.info(ProcedureMessages.PROCEDUREID_WAIT_FOR_LOCK, proc.getProcId());
402-
((ConfigNodeProcedureEnv) this.environment).getNodeLock().waitProcedure(proc);
417+
if (lockEventWaitProcedure != null) {
418+
LOG.info(ProcedureMessages.PROCEDUREID_WAIT_FOR_LOCK, lockEventWaitProcedure.getProcId());
419+
lockEventWaitProcedure.waitForLock(this.environment);
403420
}
404421
}
405422
}
@@ -607,8 +624,8 @@ private void yieldProcedure(Procedure<Env> proc) {
607624
* @param procedureStack root procedure stack
608625
* @return lock state
609626
*/
610-
private ProcedureLockState executeRootStackRollback(
611-
Long rootProcId, RootProcedureStack procedureStack) {
627+
private LockStateResult<Env> executeRootStackRollback(
628+
Long rootProcId, RootProcedureStack<Env> procedureStack) {
612629
Procedure<Env> rootProcedure = procedures.get(rootProcId);
613630
ProcedureException exception = rootProcedure.getException();
614631
if (exception == null) {
@@ -629,19 +646,19 @@ private ProcedureLockState executeRootStackRollback(
629646
}
630647
ProcedureLockState lockState = acquireLock(procedure);
631648
if (lockState != ProcedureLockState.LOCK_ACQUIRED) {
632-
return lockState;
649+
return new LockStateResult<>(lockState, procedure);
633650
}
634651
lockState = executeRollback(procedure);
635652
releaseLock(procedure, false);
636653

637654
boolean abortRollback = lockState != ProcedureLockState.LOCK_ACQUIRED;
638655
abortRollback |= !isRunning() || !store.isRunning();
639656
if (abortRollback) {
640-
return lockState;
657+
return new LockStateResult<>(lockState, procedure);
641658
}
642659

643660
if (!procedure.isFinished() && procedure.isYieldAfterExecution(this.environment)) {
644-
return ProcedureLockState.LOCK_YIELD_WAIT;
661+
return new LockStateResult<>(ProcedureLockState.LOCK_YIELD_WAIT, procedure);
645662
}
646663

647664
if (procedure != rootProcedure) {
@@ -652,7 +669,7 @@ private ProcedureLockState executeRootStackRollback(
652669
LOG.info(
653670
ProcedureMessages.ROLLED_BACK_TIME_DURATION_IS, rootProcedure, rootProcedure.elapsedTime());
654671
rootProcedureCleanup(rootProcedure);
655-
return ProcedureLockState.LOCK_ACQUIRED;
672+
return new LockStateResult<>(ProcedureLockState.LOCK_ACQUIRED, rootProcedure);
656673
}
657674

658675
private ProcedureLockState acquireLock(Procedure<Env> proc) {

iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/node/AbstractNodeProcedure.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,18 @@ protected ProcedureLockState acquireLock(ConfigNodeProcedureEnv configNodeProced
5656
}
5757
}
5858

59+
@Override
60+
protected void waitForLock(ConfigNodeProcedureEnv configNodeProcedureEnv) {
61+
configNodeProcedureEnv.getSchedulerLock().lock();
62+
try {
63+
configNodeProcedureEnv
64+
.getNodeLock()
65+
.waitProcedure(this, configNodeProcedureEnv.getScheduler());
66+
} finally {
67+
configNodeProcedureEnv.getSchedulerLock().unlock();
68+
}
69+
}
70+
5971
@Override
6072
protected void releaseLock(ConfigNodeProcedureEnv configNodeProcedureEnv) {
6173
configNodeProcedureEnv.getSchedulerLock().lock();

iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/scheduler/LockQueue.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ public boolean releaseLock(Procedure<?> procedure) {
4545
return true;
4646
}
4747

48-
public void waitProcedure(Procedure<?> procedure) {
48+
public void waitProcedure(Procedure<?> procedure, ProcedureScheduler procedureScheduler) {
49+
if (lockOwnerProcedure == null) {
50+
procedureScheduler.addFront(procedure);
51+
return;
52+
}
4953
deque.addLast(procedure);
5054
}
5155

iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/scheduler/SimpleProcedureScheduler.java

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.apache.iotdb.confignode.procedure.Procedure;
2323

2424
import java.util.concurrent.LinkedBlockingDeque;
25+
import java.util.concurrent.locks.ReentrantLock;
2526

2627
/** Simple scheduler for procedures */
2728
public class SimpleProcedureScheduler extends AbstractProcedureScheduler {
@@ -48,6 +49,7 @@ public void clear() {
4849
schedLock();
4950
try {
5051
runnables.clear();
52+
waitings.clear();
5153
} finally {
5254
schedUnlock();
5355
}
@@ -68,12 +70,37 @@ public int queueSize() {
6870
return runnables.size();
6971
}
7072

71-
public void addWaiting(Procedure proc) {
72-
waitings.add(proc);
73+
public void waitProcedure(Procedure proc, ReentrantLock lock) {
74+
boolean signal = false;
75+
schedLock();
76+
try {
77+
if (lock.isLocked()) {
78+
waitings.add(proc);
79+
} else {
80+
runnables.addFirst(proc);
81+
signal = true;
82+
}
83+
} finally {
84+
schedUnlock();
85+
}
86+
if (signal) {
87+
signalAll();
88+
}
7389
}
7490

75-
public void releaseWaiting() {
76-
runnables.addAll(waitings);
77-
waitings.clear();
91+
public void releaseWaiting(ReentrantLock lock) {
92+
boolean signal;
93+
schedLock();
94+
try {
95+
lock.unlock();
96+
signal = !waitings.isEmpty();
97+
runnables.addAll(waitings);
98+
waitings.clear();
99+
} finally {
100+
schedUnlock();
101+
}
102+
if (signal) {
103+
signalAll();
104+
}
78105
}
79106
}

iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/entity/SimpleLockProcedure.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,21 @@ protected ProcedureLockState acquireLock(TestProcEnv testProcEnv) {
5353

5454
return ProcedureLockState.LOCK_ACQUIRED;
5555
}
56-
SimpleProcedureScheduler scheduler = (SimpleProcedureScheduler) testProcEnv.getScheduler();
57-
scheduler.addWaiting(this);
5856
System.out.println(procName + " wait for lock.");
5957
return ProcedureLockState.LOCK_EVENT_WAIT;
6058
}
6159

60+
@Override
61+
protected void waitForLock(TestProcEnv testProcEnv) {
62+
SimpleProcedureScheduler scheduler = (SimpleProcedureScheduler) testProcEnv.getScheduler();
63+
scheduler.waitProcedure(this, testProcEnv.getEnvLock());
64+
}
65+
6266
@Override
6367
protected void releaseLock(TestProcEnv testProcEnv) {
6468
System.out.println(procName + " release lock.");
65-
testProcEnv.getEnvLock().unlock();
6669
SimpleProcedureScheduler scheduler = (SimpleProcedureScheduler) testProcEnv.getScheduler();
67-
scheduler.releaseWaiting();
70+
scheduler.releaseWaiting(testProcEnv.getEnvLock());
6871
}
6972

7073
@Override

0 commit comments

Comments
 (0)