Skip to content

v0.83.0

Choose a tag to compare

@github-actions github-actions released this 28 Jul 09:28
· 5 commits to refs/heads/main since this release
e7dc826

Highlights

The maintenance observer no longer holds a session on a product database — #464

A product that maintains itself typically follows this sequence: set its maintenance flag, wait until all connections to its database are closed, then take the database exclusively (SINGLE_USER, restore). RSGO's SQL observer is a participant in that handshake — it detects the flag and stops the product's containers, whose connections then go away.

But the observer's own connection was pooled: Dispose only returns it to the ADO.NET pool, and because the next poll refreshed it, the pool's idle cleanup never reclaimed it. The product's wait then never completed — the component that reacts to the flag prevented the effect of its own reaction. Where a product waits without a timeout, its update simply never finishes.

Connections RSGO opens itself are now non-pooled. Both SQL observers, the SQL setter and the SQL connection test endpoint: the session exists for the duration of a single read and is gone afterwards. Sessions are tagged with an application name (ReadyStackGo-Maintenance, ReadyStackGo-ConnectionTest) so they can be identified in sys.dm_exec_sessions or sp_who2. Product containers are unaffected — RSGO does not build their connection strings, and they keep their pooling.

RSGO no longer reads a database it must not touch. The maintenance flag usually lives inside the very database that becomes exclusive, so during that window it cannot be read — and a connect attempt can occupy the single available single-user slot and fail the product's own update with error 924. Before every read, a SQL observer now checks the database's availability in sys.databases over a master connection derived from the same credentials:

  • master stays reachable while another database is SINGLE_USER, RESTORING or OFFLINE, and the query takes no lock on the target database.
  • While the database is unavailable, the observer reports maintenance with an observed value of database-exclusive (<state>/<user access>) and leaves the database alone.
  • Once it is ONLINE and MULTI_USER again, the next poll reads the flag normally — the automatic return to normal operation keeps working, however long the product takes.
  • If availability cannot be determined (the observer login has no access to master), RSGO logs one warning and falls back to the previous behaviour, rather than parking a deployment in maintenance over a permission problem.

Manual maintenance suspends observer polling. Trigger ownership means an observer may not end manually activated maintenance, so the read could only ever leave a session behind. Entering maintenance manually is now also a reliable way to keep RSGO off a product entirely.

Fixed along the way

  • pollingInterval was ignored. Observer state lived on a scoped service while the background service creates a fresh scope every cycle, so every cycle looked like a first check — the effective interval was always 30s. State moved into a singleton store.
  • The observer status endpoint was always empty. GET /api/health/deployments/{id}/observer read the last result from a different scope and therefore always reported HasObserver=false.
  • A cached observer survived a config change. A manifest edit picked up by a redeploy now takes effect without restarting RSGO — while keeping the check timestamp, so a config change cannot be used to read a product database more often than its interval allows.
  • The connection test endpoint (POST /api/connections/test/sqlserver) left an idle session on the product database for the pool's lifetime.

No migration and no manifest change required: the availability check derives its master connection from the existing observer connection string, so the fix applies to already-deployed manifests as they are. Deployments without a SQL observer are unaffected.

Documentation: Maintenance mode → Database access during maintenance. The docs also described an observer enabled field and a PUT /api/deployments/{id}/maintenance-observer endpoint — neither exists, both removed.

Full changelog: v0.82.0...v0.83.0