releasePeerRun function

Future<void> releasePeerRun(
  1. int projectId,
  2. String runTitle,
  3. Future<void> finish(), {
  4. Map<String, String>? env,
})

Drops one participant; the last expected peer calls finish.

With peerCountEnv set, the run is held open while fewer peers have claimed than expected, so a late starter still attaches.

Implementation

Future<void> releasePeerRun(
  int projectId,
  String runTitle,
  Future<void> Function() finish, {
  Map<String, String>? env,
}) async {
  final paths = handoffPaths(projectId, runTitle, env);
  try {
    if (!File(paths.idPath).existsSync()) return;
  } on IOException {
    return;
  }
  if (!acquireHandoffLock(paths.lockPath, const Duration(seconds: 60))) {
    logDebug('Tracera: timed out waiting for handoff lock ${paths.lockPath}');
    return;
  }
  try {
    final state = _readHandoffState(paths.idPath);
    if (state == null) return;
    var refs = state.refs - 1;
    if (refs < 0) refs = 0;
    if (refs > 0) {
      // Ignore write failure — same as Go `_ = writeHandoffState`.
      _tryWriteHandoffState(paths.idPath, state.runId, refs, state.claimed);
      logDebug(
        'Tracera: peer handoff release run #${state.runId} (refs=$refs)',
      );
      return;
    }
    final expected = peerCount(env);
    if (expected > 0 && state.claimed < expected) {
      _tryWriteHandoffState(paths.idPath, state.runId, refs, state.claimed);
      logDebug(
        'Tracera: peer handoff holding run #${state.runId} open '
        '(claimed=${state.claimed}/$expected, waiting for peers)',
      );
      return;
    }
    await finish();
    _tryDeleteFile(paths.idPath);
    logDebug('Tracera: peer handoff finished run #${state.runId}');
  } finally {
    releaseHandoffLock(paths.lockPath);
  }
}