Skip to content

Commit 0a45a3b

Browse files
authored
Fix duplicate scheduling in procedure execution (#17902)
* Fix duplicate scheduling in procedure execution * Fix delayed procedure deduplication and semaphore release * Fix SQL parser error handler traversal * Fix pipe procedure lock release race * Fix procedure lock wait scheduling (cherry picked from commit c25849a)
1 parent 9ca448e commit 0a45a3b

33 files changed

Lines changed: 509 additions & 102 deletions

iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinatorLock.java

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,10 @@ public void lock() {
4747
LOGGER.debug(
4848
ManagerMessages.PIPETASKCOORDINATOR_LOCK_WAITING_FOR_THREAD,
4949
Thread.currentThread().getName());
50-
try {
51-
semaphore.acquire();
52-
LOGGER.debug(
53-
ManagerMessages.PIPETASKCOORDINATOR_LOCK_ACQUIRED_BY_THREAD,
54-
Thread.currentThread().getName());
55-
} catch (final InterruptedException e) {
56-
Thread.currentThread().interrupt();
57-
PipeLogger.log(
58-
LOGGER::error,
59-
ManagerMessages.INTERRUPTED_WHILE_WAITING_FOR_PIPETASKCOORDINATOR_LOCK_CURRENT_THREAD,
60-
Thread.currentThread().getName());
61-
}
50+
semaphore.acquireUninterruptibly();
51+
LOGGER.debug(
52+
ManagerMessages.PIPETASKCOORDINATOR_LOCK_ACQUIRED_BY_THREAD,
53+
Thread.currentThread().getName());
6254
}
6355

6456
public boolean tryLock() {

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import java.util.ArrayList;
3838
import java.util.Arrays;
3939
import java.util.List;
40+
import java.util.concurrent.atomic.AtomicBoolean;
4041
import java.util.concurrent.atomic.AtomicReference;
4142

4243
/**
@@ -62,6 +63,7 @@ public abstract class Procedure<Env> implements Comparable<Procedure<Env>> {
6263
private volatile long lastUpdate;
6364

6465
private final AtomicReference<byte[]> result = new AtomicReference<>();
66+
private final AtomicBoolean executing = new AtomicBoolean(false);
6567
private volatile boolean locked = false;
6668
private boolean lockedWhenLoading = false;
6769

@@ -235,6 +237,16 @@ protected void releaseLock(Env env) {
235237
// no op
236238
}
237239

240+
/**
241+
* Called after an execution attempt returns {@link ProcedureLockState#LOCK_EVENT_WAIT}. Override
242+
* it to put the procedure into the corresponding lock wait queue.
243+
*
244+
* @param env env
245+
*/
246+
protected void waitForLock(Env env) {
247+
// no op
248+
}
249+
238250
/**
239251
* Used to keep procedure lock even when the procedure is yielded or suspended.
240252
*
@@ -256,6 +268,14 @@ protected boolean isYieldAfterExecution(Env env) {
256268
}
257269

258270
// -------------------------Internal methods - called by the procedureExecutor------------------
271+
final boolean tryAcquireExecution() {
272+
return executing.compareAndSet(false, true);
273+
}
274+
275+
final void releaseExecution() {
276+
executing.set(false);
277+
}
278+
259279
/**
260280
* Internal method called by the ProcedureExecutor that starts the user-level code execute().
261281
*

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

Lines changed: 79 additions & 40 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);
@@ -367,19 +381,25 @@ private void executeProcedure(Procedure<Env> proc) {
367381
}
368382
break;
369383
}
370-
lockState = acquireLock(proc);
371-
switch (lockState) {
372-
case LOCK_ACQUIRED:
373-
executeProcedure(rootProcStack, proc);
374-
break;
375-
case LOCK_YIELD_WAIT:
376-
case LOCK_EVENT_WAIT:
377-
LOG.info(ProcedureMessages.LOCKSTATE_IS, proc, lockState);
378-
break;
379-
default:
380-
throw new UnsupportedOperationException();
384+
try {
385+
lockState = acquireLock(proc);
386+
switch (lockState) {
387+
case LOCK_ACQUIRED:
388+
executeProcedure(rootProcStack, proc);
389+
break;
390+
case LOCK_YIELD_WAIT:
391+
case LOCK_EVENT_WAIT:
392+
LOG.info(ProcedureMessages.LOCKSTATE_IS, proc, lockState);
393+
if (lockState == ProcedureLockState.LOCK_EVENT_WAIT) {
394+
lockEventWaitProcedure = proc;
395+
}
396+
break;
397+
default:
398+
throw new UnsupportedOperationException();
399+
}
400+
} finally {
401+
rootProcStack.release();
381402
}
382-
rootProcStack.release();
383403

384404
if (proc.isSuccess()) {
385405
// update metrics on finishing the procedure
@@ -397,9 +417,9 @@ private void executeProcedure(Procedure<Env> proc) {
397417
} finally {
398418
// Only after procedure has completed execution can it be allowed to be rescheduled to prevent
399419
// 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);
420+
if (lockEventWaitProcedure != null) {
421+
LOG.info(ProcedureMessages.PROCEDUREID_WAIT_FOR_LOCK, lockEventWaitProcedure.getProcId());
422+
lockEventWaitProcedure.waitForLock(this.environment);
403423
}
404424
}
405425
}
@@ -414,6 +434,7 @@ private void executeProcedure(RootProcedureStack rootProcStack, Procedure<Env> p
414434
if (proc.getState() != ProcedureState.RUNNABLE) {
415435
LOG.error(
416436
"The executing procedure should in RUNNABLE state, but it's not. Procedure is {}", proc);
437+
releaseLock(proc, false);
417438
return;
418439
}
419440
boolean reExecute;
@@ -615,8 +636,8 @@ private void yieldProcedure(Procedure<Env> proc) {
615636
* @param procedureStack root procedure stack
616637
* @return lock state
617638
*/
618-
private ProcedureLockState executeRootStackRollback(
619-
Long rootProcId, RootProcedureStack procedureStack) {
639+
private LockStateResult<Env> executeRootStackRollback(
640+
Long rootProcId, RootProcedureStack<Env> procedureStack) {
620641
Procedure<Env> rootProcedure = procedures.get(rootProcId);
621642
ProcedureException exception = rootProcedure.getException();
622643
if (exception == null) {
@@ -637,19 +658,19 @@ private ProcedureLockState executeRootStackRollback(
637658
}
638659
ProcedureLockState lockState = acquireLock(procedure);
639660
if (lockState != ProcedureLockState.LOCK_ACQUIRED) {
640-
return lockState;
661+
return new LockStateResult<>(lockState, procedure);
641662
}
642663
lockState = executeRollback(procedure);
643664
releaseLock(procedure, false);
644665

645666
boolean abortRollback = lockState != ProcedureLockState.LOCK_ACQUIRED;
646667
abortRollback |= !isRunning() || !store.isRunning();
647668
if (abortRollback) {
648-
return lockState;
669+
return new LockStateResult<>(lockState, procedure);
649670
}
650671

651672
if (!procedure.isFinished() && procedure.isYieldAfterExecution(this.environment)) {
652-
return ProcedureLockState.LOCK_YIELD_WAIT;
673+
return new LockStateResult<>(ProcedureLockState.LOCK_YIELD_WAIT, procedure);
653674
}
654675

655676
if (procedure != rootProcedure) {
@@ -660,7 +681,7 @@ private ProcedureLockState executeRootStackRollback(
660681
LOG.info(
661682
ProcedureMessages.ROLLED_BACK_TIME_DURATION_IS, rootProcedure, rootProcedure.elapsedTime());
662683
rootProcedureCleanup(rootProcedure);
663-
return ProcedureLockState.LOCK_ACQUIRED;
684+
return new LockStateResult<>(ProcedureLockState.LOCK_ACQUIRED, rootProcedure);
664685
}
665686

666687
private ProcedureLockState acquireLock(Procedure<Env> proc) {
@@ -795,21 +816,38 @@ public void run() {
795816
Thread.sleep(1000);
796817
continue;
797818
}
798-
this.activeProcedure.set(procedure);
799-
activeExecutorCount.incrementAndGet();
800-
startTime.set(System.currentTimeMillis());
801-
PROCEDURE_EXECUTION_CONTEXT.set(true);
819+
boolean executionAcquired = false;
820+
while (isRunning() && !(executionAcquired = procedure.tryAcquireExecution())) {
821+
Thread.sleep(10);
822+
}
823+
if (!executionAcquired) {
824+
continue;
825+
}
802826
try {
803-
executeProcedure(procedure);
804-
} finally {
805-
PROCEDURE_EXECUTION_CONTEXT.remove();
827+
this.activeProcedure.set(procedure);
828+
activeExecutorCount.incrementAndGet();
829+
startTime.set(System.currentTimeMillis());
830+
try {
831+
PROCEDURE_EXECUTION_CONTEXT.set(true);
832+
try {
833+
executeProcedure(procedure);
834+
} finally {
835+
PROCEDURE_EXECUTION_CONTEXT.remove();
836+
}
837+
} finally {
838+
procedure.releaseExecution();
839+
activeExecutorCount.decrementAndGet();
840+
LOG.trace(
841+
"Halt pid={}, activeCount={}", procedure.getProcId(), activeExecutorCount.get());
842+
this.activeProcedure.set(null);
843+
lastUpdated = System.currentTimeMillis();
844+
startTime.set(lastUpdated);
845+
}
846+
} catch (Exception e) {
847+
LOG.warn(
848+
"Exception happened when worker {} execute procedure {}", getName(), procedure, e);
849+
throw e;
806850
}
807-
activeExecutorCount.decrementAndGet();
808-
LOG.trace(
809-
"Halt pid={}, activeCount={}", procedure.getProcId(), activeExecutorCount.get());
810-
this.activeProcedure.set(null);
811-
lastUpdated = System.currentTimeMillis();
812-
startTime.set(lastUpdated);
813851
}
814852

815853
} catch (Exception e) {
@@ -820,6 +858,7 @@ public void run() {
820858
this.activeProcedure.get(),
821859
e);
822860
}
861+
this.activeProcedure.set(null);
823862
} finally {
824863
LOG.info(ProcedureMessages.PROCEDURE_WORKER_TERMINATED, getName());
825864
}

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

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,13 @@ public TimeoutExecutorThread(
4343
}
4444

4545
public void add(Procedure<Env> procedure) {
46-
queue.add(new ProcedureDelayContainer<>(procedure));
46+
ProcedureDelayContainer<Env> delayTask = new ProcedureDelayContainer<>(procedure);
47+
queue.remove(delayTask);
48+
queue.add(delayTask);
4749
}
4850

4951
public boolean remove(Procedure<Env> procedure) {
50-
return queue.remove(new ProcedureDelayContainer<>(procedure));
52+
return queue.remove(new ProcedureDelayContainer<>(procedure)) || procedure.isFinished();
5153
}
5254

5355
private ProcedureDelayContainer<Env> takeQuietly() {
@@ -68,10 +70,15 @@ public void run() {
6870
}
6971
Procedure<Env> procedure = delayTask.getProcedure();
7072
if (procedure instanceof InternalProcedure) {
73+
if (procedure.isFinished()) {
74+
continue;
75+
}
7176
InternalProcedure internal = (InternalProcedure) procedure;
7277
internal.periodicExecute(executor.getEnvironment());
73-
procedure.updateTimestamp();
74-
queue.add(delayTask);
78+
if (!procedure.isFinished()) {
79+
procedure.updateTimestamp();
80+
queue.add(delayTask);
81+
}
7582
} else {
7683
if (procedure.setTimeoutFailure(executor.getEnvironment())) {
7784
long rootProcId = executor.getRootProcedureId(procedure);
@@ -104,6 +111,23 @@ public Procedure<Env> getProcedure() {
104111
return procedure;
105112
}
106113

114+
@Override
115+
public boolean equals(Object o) {
116+
if (this == o) {
117+
return true;
118+
}
119+
if (!(o instanceof ProcedureDelayContainer)) {
120+
return false;
121+
}
122+
ProcedureDelayContainer<?> that = (ProcedureDelayContainer<?>) o;
123+
return procedure == that.procedure;
124+
}
125+
126+
@Override
127+
public int hashCode() {
128+
return System.identityHashCode(procedure);
129+
}
130+
107131
@Override
108132
public long getDelay(TimeUnit unit) {
109133
long delay = procedure.getTimeoutTimestamp() - System.currentTimeMillis();

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ private void addNextStateAndCalculateCycles() {
195195
nextState);
196196
}
197197
}
198-
if (getStateId(getCurrentState()) == stateToBeAdded) {
198+
final TState currentState = getCurrentState();
199+
if (currentState != null && getStateId(currentState) == stateToBeAdded) {
199200
cycles++;
200201
} else {
201202
cycles = 0;

iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/cq/CreateCQProcedure.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ public boolean equals(Object o) {
299299
}
300300
CreateCQProcedure that = (CreateCQProcedure) o;
301301
return getProcId() == that.getProcId()
302-
&& getCurrentState().equals(that.getCurrentState())
302+
&& Objects.equals(getCurrentState(), that.getCurrentState())
303303
&& getCycles() == that.getCycles()
304304
&& isGeneratedByPipe == that.isGeneratedByPipe
305305
&& firstExecutionTime == that.firstExecutionTime

0 commit comments

Comments
 (0)