Skip to content

fix(update): stop a second update running while one is in progress - #5095

Open
dgibbs64 wants to merge 1 commit into
developfrom
fix/update-lock
Open

dgibbs64 wants to merge 1 commit into
developfrom
fix/update-lock

Conversation

@dgibbs64

Copy link
Copy Markdown
Member

Description

Stops a second update, force-update, validate or workshop-update from running SteamCMD while another one is already running, and fixes two bugs in the SteamCMD retry loop that made a failing base app retry forever.

Update lock. The new check_update_lock.sh runs right after check.sh / core_logs.sh in command_update.sh, command_validate.sh and command_workshop_update.sh. It writes $$ to lgsm/lock/updating.lock with noclobber, so two runs started at the same moment cannot both take the lock. If the lock is held, the second run prints Checking update: Update is already running (PID n) and exits 0, with no alert. Works the same way as <selfname>-warning.lock:

  • A lock whose PID is dead is removed (logged as a warning) and the run carries on.
  • A lock whose PID was reused by a process started after the lock was written (ps -o etimes) is also treated as stale. In a container the PIDs repeat after a restart, so a lock left by a killed update could otherwise match an unrelated process and block updates for good.
  • core_exit.sh removes the lock when the command that took it exits, including on CTRL-C. Nested calls (start with updateonstart, mods-remove running validate, the stop/start inside fn_update_apply) see their own PID and do not release it early.

The lock is not per-instance, like update.lock, because instances in the same rootdir share serverfiles. update.lock and monitor's fn_monitor_check_update are unchanged.

SteamCMD retry loop (fn_dl_steamcmd).

  • When the baseappid download failed, the loop ran continue, which skipped the error report and the 10 retry limit, so a failing base app retried forever with no message. This kept the tf2cserver update running for an hour. The main app now only downloads when the base app succeeded, and the error check and retry limit run for every attempt.
  • ${selfname}-steamcmd.log was only cleared before the loop, and the check counted every Error! in the accumulated log against the attempt counter. After an attempt that printed two different errors, a later successful attempt still reported "Update required but not completed - check network". That error also sets a non-zero exit code, so SteamCMD ran again after a download that had already worked. The log is now cleared before every attempt, and each attempt is checked against its own output only.

Fixes #5094

Affected servers: all SteamCMD servers

Type of change

  • Bug fix (a change which fixes an issue).
  • New feature (a change which adds functionality).
  • New Server (new server added).
  • Refactor (restructures existing code).
  • Chore (CI, workflows, tooling or other maintenance).
  • Comment update (typo, spelling, explanation, examples, etc).

Testing

  • Commands/tests run: a throwaway gameservermanagers/gameserver:vh container (Valheim, anonymous SteamCMD, 2.2 GB), with --no-healthcheck, UPDATE_CHECK=0 and no published ports. The develop and branch lgsm/modules were copied in in turn. Serverfiles were wiped, a force-update was started in the background, and a second command was run 10 s into the SteamCMD download. SteamCMD processes were listed 15 s into the second command.
  • Result:

Concurrency, develop: the second force-update starts its own SteamCMD into the same serverfiles:

-- SteamCMD processes 15s into the second command:
  11005      26 bash .../steamcmd.sh +force_install_dir /data/serverfiles +login anonymous  +app_update 896660 +quit
  11013      26 .../linux32/steamcmd +force_install_dir /data/serverfiles +login anonymous  +app_update 896660 +quit
  12250       2 bash .../steamcmd.sh +force_install_dir /data/serverfiles +login anonymous  +app_update 896660 +quit
  12258       2 .../linux32/steamcmd +force_install_dir /data/serverfiles +login anonymous  +app_update 896660 +quit

Concurrency, branch: update, force-update and validate each exit 0 without starting SteamCMD (only the first run's SteamCMD is listed):

== second: update (while first is downloading)
-- SteamCMD processes 15s into the second command:
  14029      26 bash .../steamcmd.sh +force_install_dir /data/serverfiles +login anonymous  +app_update 896660 +quit
  14037      26 .../linux32/steamcmd +force_install_dir /data/serverfiles +login anonymous  +app_update 896660 +quit
exit code: 0 after 15s
[ INFO ] Updating vhserver: Checking update: Update is already running (PID 12855) ... INFO

== second: force-update (while first is downloading)
exit code: 0 after 15s
[ INFO ] Updating vhserver: Checking update: Update is already running (PID 12855) ... INFO

== second: validate (while first is downloading)
exit code: 0 after 15s
[ INFO ] Validating vhserver: Checking update: Update is already running (PID 12855) ... INFO

workshop-update gets the same check, but it was not tested here because Valheim has no Workshop support.

The first run then finished (Success! App '896660' fully installed.) and updating.lock was gone. A normal update afterwards ran as usual ("No update available", exit 0) and removed its lock.

Stale locks, branch:

Lock contents Result
Dead PID 999999 Removed, update runs: WARN: Checking update: Removing stale lockfile ...
Live sleep started after the lock was written (lock mtime 10 minutes old) Removed as a reused PID, update runs
Live sleep started before the lock was written Update is already running (PID 20438), exit 0

Retry loop: a stub steamcmd earlier in PATH failed chosen calls with ERROR! Timed out waiting for AppInfo update. and Error! App '<id>' state is 0x602 after update job., and passed the rest to the real SteamCMD.

Case develop branch
App 896660 fails twice, then succeeds Attempt 3 succeeds but still prints "Update required but not completed", SteamCMD runs 5 times 2 errors, then Success!, SteamCMD runs 3 times
baseappid="1007" always fails No error printed, no retry limit: 999 base app attempts until the stub stopped failing Error printed for each attempt, then Did not complete the download, too many retrys after 11 attempts, exit 1
  • Static checks: shellcheck -x and shfmt -d -sr are clean on every changed file, and all modules stay 100755.
  • Environment used (distro/version): gameservermanagers/gameserver:vh (Ubuntu 24.04 base), Docker 29.4 on Ubuntu 26.04.

Risk and rollback

  • Risk level: medium. The lock is new code on every update/validate path. A stuck lock is only possible if the PID in it belongs to a live process that started before the lock was written. Deleting lgsm/lock/updating.lock clears it.
  • Rollback plan: revert this PR.

Breaking changes

  • No breaking changes.
  • Breaking changes included (describe below).

Documentation

Checklist

PR will not be merged until all steps are complete.

  • This pull request links to an issue ("Fixes", or "Related to" for a partial fix).
  • This pull request uses the develop branch as its base.
  • This pull request subject follows the Conventional Commits standard.
  • This code follows the style guidelines of this project.
  • I have performed a self-review of my code.
  • I have provided a detailed enough description of this PR.

Thank you for your Pull Request!

A second update, force-update, validate or workshop-update started while
another was running (for example the docker entrypoint update and the cron
update) ran SteamCMD into the same serverfiles at the same time and broke
both downloads. The new check_update_lock.sh writes the PID to
lock/updating.lock with noclobber, and a second run prints "Update is already
running" and exits 0. A lock whose PID is dead, or was reused by a process
started after the lock was written, is removed. core_exit.sh removes the lock
when the owning command exits.

fn_dl_steamcmd fixes:
- A failing baseappid download ran continue, which skipped the error report
  and the retry limit, so it retried forever without a message.
- The SteamCMD log was only cleared before the loop and every Error! in it
  was counted against the attempt counter. After an attempt with two
  different errors, a later successful attempt still reported "Update
  required but not completed" and SteamCMD ran again. The log is now cleared
  before every attempt.
@dgibbs64 dgibbs64 added this to the v26.5.0 milestone Oct 18, 2026
@github-actions github-actions Bot added type: bugfix Pull request that fixes a bug engine: source engine: unity3d Issues related to unity3d engine game: Team Fortress 2 Classified Issues related to Team Fortress 2 Classified game: Valheim Issues related to Valheim labels Oct 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

engine: source engine: unity3d Issues related to unity3d engine game: Team Fortress 2 Classified Issues related to Team Fortress 2 Classified game: Valheim Issues related to Valheim type: bugfix Pull request that fixes a bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant