Locking, Mutual Exclusion, and Duplicate-Run Protection
Cron overlap, CI retries, and manual reruns can execute the same automation concurrently. Locking defines which instance owns a critical section and what other instances should do while it is busy.
Learning objectives
By the end of this lesson
- Explain mutual exclusion.
- Implement a portable mkdir lock.
- Use flock where available.
- Choose fail, skip, or wait behavior.
- Recognize when distributed coordination is required.
1. Duplicate execution can corrupt shared state
Cron overlap, CI retries, manual reruns, and supervisor restarts can launch the same script simultaneously. If both instances mutate the same files or remote resources, race conditions can produce inconsistent results.
flowchart LR
A["instance A"] --> L{"lock"}
B["instance B"] --> L
L -->|"winner"| R["critical section"]
L -->|"loser"| W["wait / fail / skip"]2. mkdir can act as a portable lock primitive
lockdir=/tmp/myjob.lock
if mkdir -- "$lockdir" 2>/dev/null; then
printf 'lock acquired\n'
else
printf 'another instance is running\n' >&2
exit 75
fiDirectory creation is atomic enough on typical local filesystems to make this a useful portable pattern. It still needs stale-lock handling.
3. Release a lock through cleanup
lockdir=/tmp/myjob.lock
cleanup() {
local status=$?
rm -rf -- "$lockdir"
exit "$status"
}
mkdir -- "$lockdir" 2>/dev/null || exit 75
trap cleanup EXITDo not register lock cleanup until acquisition succeeds.
4. Lock metadata helps diagnose stale owners
printf 'pid=%s\nstarted=%s\n' \
"$$" \
"$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
> "$lockdir/owner"Metadata is diagnostic, not proof that the recorded PID still represents the original process. PID reuse makes naive stale-lock deletion risky.
5. flock provides kernel-backed advisory locking on Linux
exec 9>"/var/lock/myjob.lock"
if flock -n 9; then
printf 'lock acquired\n'
else
printf 'job already running\n' >&2
exit 75
fiThe lock is associated with the open file descriptor and released automatically when it closes.
flock is common on Linux through util-linux but is not a POSIX shell utility and is not universally available.
6. Decide whether callers wait, fail, or skip
7. Lock acquisition itself may need a timeout
if flock -w 30 9; then
run_critical_section
else
printf 'lock wait timed out\n' >&2
exit 75
fiNever let a coordination primitive accidentally create an infinite wait unless that is explicitly acceptable.
8. Keep the critical section as small as practical
prepare_inputs
acquire_lock
update_shared_state
release_lock
publish_reportDo expensive independent work outside the lock so one slow instance does not block others unnecessarily.
9. Local file locks do not coordinate multiple hosts
A lock under /tmp or a local filesystem protects only processes sharing that lock domain. Multi-host coordination may require database locks, distributed leases, orchestrator primitives, or remote state services.
The synchronization mechanism must cover every actor that can mutate the protected resource.
10. Locks and atomic updates solve different problems
Atomic rename prevents readers from seeing partially written content. A lock prevents multiple writers from simultaneously deciding or modifying state. Robust workflows may need both.
11. Hands-on lab: duplicate-run protection
mkdir -p "$HOME/devops-academy/bash/chapter12/lesson04"
cd "$HOME/devops-academy/bash/chapter12/lesson04"
cat > single-run.sh <<'EOF'
#!/usr/bin/env bash
set -u
lockdir="./job.lock"
owned=false
cleanup() {
local status=$?
if [[ $owned == true ]]; then
rm -rf -- "$lockdir"
fi
exit "$status"
}
trap cleanup EXIT
if mkdir -- "$lockdir" 2>/dev/null; then
owned=true
else
printf 'another instance is active\n' >&2
exit 75
fi
printf 'pid=%s\n' "$$" > "$lockdir/owner"
printf 'running pid=%s\n' "$$"
sleep 5
printf 'done pid=%s\n' "$$"
EOF
chmod u+x single-run.sh
printf 'Run ./single-run.sh twice quickly to observe lock rejection.\n'Verification checklist
12. Knowledge check
Question 1. What problem does a lock solve?
Question 2. Why is PID metadata not sufficient proof of lock ownership?
Question 3. Does a local /tmp lock coordinate multiple hosts?
Question 4. How are atomic rename and locking different?
13. Summary
Duplicate-run protection requires a clearly defined lock domain, ownership lifecycle, busy policy, and stale-state strategy. Portable mkdir locks are simple; kernel-backed flock is convenient where available. Keep the critical section narrow and remember that local locks do not solve distributed coordination.
14. Further reading
- util-linux documentation — flock.
- GNU Coreutils manual — mkdir and file operations.
- POSIX filesystem atomicity concepts.
- systemd documentation — service instance and timer overlap controls.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this address.