Skip to content

Commit d30806f

Browse files
committed
Utilization history survives an upgrade
Every Utilization row was saved under the container's hostname, which Docker sets to the container id. Each install.sh --upgrade made a new container, so the charts read back a new name and came up empty while the old rows sat in the database under names nobody asked for. The name is now GCR_NODE_NAME, set in docker-compose.yml and defaulting to github-code-review. A bare container id for a hostname falls back to that same name. A job at boot moves rows saved under old container id names onto the current one, keeping one row where two names share a time slot. A real host's name sharing the database is left alone.
1 parent ed01a9d commit d30806f

3 files changed

Lines changed: 74 additions & 3 deletions

File tree

‎app/core/scheduler.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,10 @@ def start() -> None:
323323
from app.core import utilization
324324

325325
now = datetime.now(_UTC)
326+
# Once at boot, history saved under earlier container names comes home.
327+
_scheduler.add_job(utilization.adopt_orphans, "date", run_date=now,
328+
id="utilization-adopt", replace_existing=True,
329+
misfire_grace_time=600)
326330
for job_id, func, trigger, first in (
327331
("utilization-sample", utilization.sample,
328332
IntervalTrigger(seconds=utilization.PERIOD_SEC), now),

‎app/core/utilization.py‎

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,26 @@
3939
MAX_POINTS = 1440
4040
MAX_SPAN_SEC = 366 * DAY
4141

42-
NODE = (re.sub(r"[^A-Za-z0-9._-]", "_",
43-
os.environ.get("GCR_NODE_NAME") or socket.gethostname())[:64]
44-
or "node")
42+
# The name every row is saved under, and the only name the charts read back.
43+
# It has to outlive the container. Docker names a container's host after the
44+
# first twelve hex digits of its id, which is new on every rebuild, so keying
45+
# on that hid all the history behind a fresh name after each install.sh
46+
# --upgrade. docker-compose.yml sets GCR_NODE_NAME, and a bare container id
47+
# falls back to a fixed name for anyone running the image some other way.
48+
DEFAULT_NODE = "github-code-review"
49+
_CONTAINER_ID = re.compile(r"^[0-9a-f]{12}$")
50+
51+
52+
def _node_name():
53+
name = os.environ.get("GCR_NODE_NAME", "").strip()
54+
if not name:
55+
name = socket.gethostname()
56+
if _CONTAINER_ID.match(name):
57+
name = DEFAULT_NODE
58+
return re.sub(r"[^A-Za-z0-9._-]", "_", name)[:64] or DEFAULT_NODE
59+
60+
61+
NODE = _node_name()
4562

4663
AVERAGED = ("host_cpu", "app_cpu", "host_mem_used", "app_mem", "disk_used",
4764
"net_rx_bps", "net_tx_bps")
@@ -326,6 +343,50 @@ def rollup(now=None):
326343
log.warning("utilization rollup failed: %s", exc)
327344

328345

346+
def adopt_orphans():
347+
"""Move rows saved under an old container id onto this node.
348+
349+
Before the name was fixed every rebuild started a new one, so the history
350+
up to now sits under dozens of twelve hex digit names nobody reads. Only
351+
names shaped like a container id are taken, never a real host's name,
352+
so two machines sharing one database keep their own rows. Where two
353+
names hold the same slot, the one already here wins and the other is
354+
dropped, since the key is the node and the time. Safe to run on every
355+
boot: once there is nothing left to move it does nothing.
356+
"""
357+
moved = 0
358+
try:
359+
for tier in TIERS:
360+
model = tier["model"]
361+
with session_scope() as db:
362+
nodes = [n for (n,) in db.query(model.node).distinct()
363+
if n != NODE and _CONTAINER_ID.match(n or "")]
364+
if not nodes:
365+
continue
366+
taken = {ts for (ts,) in
367+
db.query(model.ts).filter(model.node == NODE)}
368+
for node in nodes:
369+
with session_scope() as db:
370+
stamps = [ts for (ts,) in
371+
db.query(model.ts).filter(model.node == node)]
372+
clash = [ts for ts in stamps if ts in taken]
373+
for i in range(0, len(clash), 500):
374+
(db.query(model)
375+
.filter(model.node == node,
376+
model.ts.in_(clash[i:i + 500]))
377+
.delete(synchronize_session=False))
378+
moved += (db.query(model).filter(model.node == node)
379+
.update({model.node: NODE},
380+
synchronize_session=False))
381+
taken.update(stamps)
382+
if moved:
383+
log.info("utilization: %d rows from old container names now "
384+
"belong to %s", moved, NODE)
385+
except Exception as exc:
386+
log.warning("could not adopt old utilization rows: %s", exc)
387+
return moved
388+
389+
329390
def _combine(rows):
330391
out = {}
331392
for key in AVERAGED:

‎docker-compose.yml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,12 @@ services:
164164
GCR_GATE_TIMEOUT: ${GCR_GATE_TIMEOUT:-}
165165
GCR_GATE_WORKERS: ${GCR_GATE_WORKERS:-}
166166

167+
# The name the Utilization history is saved under. It must stay the
168+
# same across rebuilds, or each install.sh --upgrade starts the charts
169+
# from empty. Give each instance its own name when several share one
170+
# database.
171+
GCR_NODE_NAME: ${GCR_NODE_NAME:-github-code-review}
172+
167173
TZ: ${TZ:-UTC}
168174

169175
volumes:

0 commit comments

Comments
 (0)