Prevent Concurrent Cron Jobs Using `flock`

HongHong
4 min read

flock is a utility in Unix-like operating systems (like Linux) used for advisory file locking. Its primary purpose is to prevent multiple instances of a script or process from concurrently accessing and potentially corrupting a shared file or resource.

Here's a breakdown of what flock is and why it's important:

The Problem: Race Conditions and Data Corruption

Imagine you have a shell script that updates a log file or a configuration file. If this script is run by multiple processes simultaneously (e.g., from different cron jobs, or by multiple users), you could run into a "race condition":

  • Process A reads the file.
  • Process B also reads the file (before A has finished writing).
  • Process A modifies its copy of the data and writes it back to the file.
  • Process B (still working with the old data) modifies its copy and writes it back, potentially overwriting Process A's changes or causing inconsistent data.

This can lead to data loss, corruption, or unpredictable behavior.

How flock Solves This: Advisory Locks

flock addresses this by implementing advisory locks. Here's how it works:

  1. Lock File: You designate a specific file (often an empty "lock file") that all cooperating processes will attempt to lock before proceeding.
  2. Acquire Lock: When a script starts, it attempts to acquire a lock on this designated file using flock.
  3. Two Types of Locks:
    • Exclusive Lock (-x or default): Only one process can hold an exclusive lock on a file at a time. If another process tries to acquire an exclusive lock while one is held, it will either wait (default behavior) or fail immediately (-n, non-blocking). This is typically used for write operations.
    • Shared Lock (-s): Multiple processes can hold a shared lock on a file simultaneously. However, no process can hold an exclusive lock while a shared lock is active, and vice versa. This is typically used for read operations where multiple readers are fine, but a writer needs exclusive access.
  4. Execute Command: Once the lock is acquired, the script proceeds with its critical operations (e.g., modifying the shared file).
  5. Release Lock: When the script finishes, the lock is automatically released when the file descriptor associated with the lock file is closed (which happens when the script exits). You can also explicitly release a lock with -u.

Key Characteristics and Use Cases

  • Advisory Nature: It's important to understand that flock provides advisory locking. This means the operating system doesn't force processes to obey the lock. For flock to be effective, all processes that access the shared resource must explicitly use flock to acquire a lock before accessing the resource. If a process ignores flock, it can still read or write the file, potentially leading to corruption.
  • Preventing Concurrent Cron Jobs: A very common use case is to ensure that a cron job doesn't run on top of itself if a previous instance is still running.

    #!/bin/bash
    LOCKFILE="/var/lock/my_script.lock"
    
    # Try to acquire an exclusive, non-blocking lock.
    # If the lock is already held, exit immediately (do not run).
    exec 200>$LOCKFILE
    flock -n 200 || exit 1
    
    # If we reached here, the lock was acquired.
    echo "Script started at $(date)" >> /var/log/my_script.log
    
    # Simulate some work that takes time
    sleep 30
    
    echo "Script finished at $(date)" >> /var/log/my_script.log
    
    # The lock is automatically released when the script exits,
    # because file descriptor 200 is closed.
    
  • Synchronization: It can synchronize access to any shared resource (not just files), as long as all participating processes agree on a common "lock file" to manage access.
  • File Descriptors: flock can operate on a file path directly or, more commonly, on an open file descriptor (like in the example above, where exec 200>$LOCKFILE opens the lock file on file descriptor 200). Operating on a file descriptor is often preferred because the lock is tied to the descriptor and automatically released when the descriptor is closed (e.g., when the script exits), even if the script crashes.

In summary, flock is a fundamental tool for managing concurrent access to shared resources in shell scripting, providing a cooperative mechanism to maintain data integrity and system stability.

Pro-tip

Thanks for making it this far ๐Ÿ˜‰. Systemd timer is encouraged over cron job. If a timer is still running when the next scheduled trigger comes up, it will not start a second instance. Therefore flock is not needed here.

0
Subscribe to my newsletter

Read articles from Hong directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Hong
Hong

I am a developer from Malaysia. I work with PHP most of the time, recently I fell in love with Go. When I am not working, I will be ballroom dancing :-)