91 lines
2.8 KiB
Python
91 lines
2.8 KiB
Python
"""Трекер созданных инстансов — файловый с fcntl.flock (multi-worker gunicorn)."""
|
||
import fcntl
|
||
import json
|
||
import os
|
||
import time
|
||
|
||
_PATH = "/tmp/instances.json"
|
||
|
||
_INITIAL = {
|
||
"408b7f96-6ed7-4985-be1b-f5bdcb6ab44d": {"svcId": 1, "displayName": "autotest-1", "instanceUid": "408b7f96-6ed7-4985-be1b-f5bdcb6ab44d"},
|
||
"884356cf-5212-4395-b56b-27c58a5fc1fa": {"svcId": 1, "displayName": "autotest-1-first", "instanceUid": "884356cf-5212-4395-b56b-27c58a5fc1fa"},
|
||
"6528854d-a4ea-428c-9fa4-68e85fa9b3ac": {"svcId": 1, "displayName": "curl-test-144834", "instanceUid": "6528854d-a4ea-428c-9fa4-68e85fa9b3ac"},
|
||
"fdcb5887-39f7-4ef8-a9c0-d55e434a55ff": {"svcId": 1, "displayName": "curl-final-test", "instanceUid": "fdcb5887-39f7-4ef8-a9c0-d55e434a55ff"},
|
||
}
|
||
|
||
|
||
def _acquire_lock(fd):
|
||
"""LOCK_EX с таймаутом 2 секунды (LOCK_NB + retry)."""
|
||
deadline = time.time() + 2
|
||
while True:
|
||
try:
|
||
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||
return True
|
||
except BlockingIOError:
|
||
if time.time() >= deadline:
|
||
return False
|
||
time.sleep(0.05)
|
||
|
||
|
||
def _locked_read():
|
||
"""Читает файл под эксклюзивной блокировкой, seed при отсутствии."""
|
||
try:
|
||
fd = os.open(_PATH, os.O_RDWR | os.O_CREAT, 0o644)
|
||
except OSError:
|
||
return dict(_INITIAL)
|
||
try:
|
||
if not _acquire_lock(fd):
|
||
return dict(_INITIAL)
|
||
try:
|
||
data = os.read(fd, 65536)
|
||
if data:
|
||
return json.loads(data.decode("utf-8"))
|
||
except (json.JSONDecodeError, UnicodeDecodeError):
|
||
pass
|
||
# Файл пуст или битый — пишем _INITIAL
|
||
data = dict(_INITIAL)
|
||
os.lseek(fd, 0, 0)
|
||
os.ftruncate(fd, 0)
|
||
os.write(fd, json.dumps(data, indent=2).encode("utf-8"))
|
||
return data
|
||
finally:
|
||
fcntl.flock(fd, fcntl.LOCK_UN)
|
||
os.close(fd)
|
||
|
||
|
||
def _locked_write(data):
|
||
"""Пишет файл под эксклюзивной блокировкой."""
|
||
try:
|
||
fd = os.open(_PATH, os.O_RDWR | os.O_CREAT, 0o644)
|
||
except OSError:
|
||
return
|
||
try:
|
||
if not _acquire_lock(fd):
|
||
return
|
||
os.lseek(fd, 0, 0)
|
||
os.ftruncate(fd, 0)
|
||
os.write(fd, json.dumps(data, indent=2).encode("utf-8"))
|
||
finally:
|
||
fcntl.flock(fd, fcntl.LOCK_UN)
|
||
os.close(fd)
|
||
|
||
|
||
def add(instance_uid, svc_id, display_name):
|
||
data = _locked_read()
|
||
data[instance_uid] = {
|
||
"svcId": svc_id,
|
||
"displayName": display_name,
|
||
"instanceUid": instance_uid,
|
||
}
|
||
_locked_write(data)
|
||
|
||
|
||
def remove(instance_uid):
|
||
data = _locked_read()
|
||
data.pop(instance_uid, None)
|
||
_locked_write(data)
|
||
|
||
|
||
def list_all():
|
||
return list(_locked_read().values())
|