Preserve server status when batch probes miss the deadline
This commit is contained in:
@@ -32,7 +32,7 @@ import urllib.parse
|
||||
import urllib.request
|
||||
import uuid
|
||||
|
||||
VERSION = "1.4.12"
|
||||
VERSION = "1.4.13"
|
||||
LABEL = "io.electric-cherry"
|
||||
MAX_FILE = 32 * 1024 * 1024
|
||||
MANIFEST_MAX_ENTRIES = 10000
|
||||
@@ -1368,7 +1368,7 @@ class Agent:
|
||||
def probe(identity):
|
||||
instance_id, generation = identity
|
||||
target = {"instanceId": instance_id, "generation": generation}
|
||||
reply = {"ok": False, "reason": "timeout", "text": ""}
|
||||
reply = {"ok": False, "reason": "deadline", "text": ""}
|
||||
# A batch does not hold every instance lock while one server waits.
|
||||
# Bound concurrency across overlapping batches as well as within one.
|
||||
lock = self.instance_lock(target)
|
||||
@@ -1382,7 +1382,7 @@ class Agent:
|
||||
if current.get("generation", generation) != generation or current.get("removed"):
|
||||
reply = {"ok": False, "reason": "superseded", "text": ""}
|
||||
elif time.monotonic() < deadline:
|
||||
reply = self.rcon(instance_id, generation, "status", deadline=deadline)
|
||||
reply = self.rcon(instance_id, generation, "status", deadline=deadline, status_attempts=1)
|
||||
except (AgentError, OSError, ValueError, KeyError):
|
||||
reply = {"ok": False, "reason": "unreachable", "text": ""}
|
||||
finally:
|
||||
@@ -1395,7 +1395,7 @@ class Agent:
|
||||
results = list(executor.map(probe, identities))
|
||||
return {"ok": all(row["reply"].get("ok") for row in results), "results": results}
|
||||
|
||||
def rcon(self, instance_id, generation, command, deadline=None):
|
||||
def rcon(self, instance_id, generation, command, deadline=None, status_attempts=3):
|
||||
if not isinstance(command, str) or not command or len(command) > 2048 or any(ch in command for ch in ("\x00", "\r", "\n")):
|
||||
raise AgentError("Invalid RCON command")
|
||||
saved = self.spec(instance_id, generation)
|
||||
@@ -1407,11 +1407,12 @@ class Agent:
|
||||
family = socket.AF_INET6 if ":" in host else socket.AF_INET
|
||||
# Retry only the read-only occupancy query. A missing UDP reply must
|
||||
# never cause commands with effects to be replayed.
|
||||
for attempt in range(3 if command == "status" else 1):
|
||||
attempts = status_attempts if command == "status" else 1
|
||||
for attempt in range(attempts):
|
||||
if deadline is not None and time.monotonic() >= deadline:
|
||||
return {"ok": False, "reason": "timeout", "players": None, "text": ""}
|
||||
return {"ok": False, "reason": "deadline" if attempt == 0 else "timeout", "players": None, "text": ""}
|
||||
result = self.rcon_attempt(family, host, int(saved["port"]), password, command, **({"deadline": deadline} if deadline is not None else {}))
|
||||
if result["ok"] or result.get("reason") not in ("timeout", "unreachable") or attempt == 2 or command != "status":
|
||||
if result["ok"] or result.get("reason") not in ("timeout", "unreachable") or attempt == attempts - 1:
|
||||
return result
|
||||
time.sleep(min(0.25 * 2 ** attempt, max(0, deadline - time.monotonic())) if deadline is not None else 0.25 * 2 ** attempt)
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"version": "1.4.12",
|
||||
"version": "1.4.13",
|
||||
"protocolVersion": 1,
|
||||
"platform": "debian-amd64",
|
||||
"sha256": "1e1bf2f04c02f0c8d0db8e4dbf39fadddc39c8bd04db5b8db753b6fe9e04c315",
|
||||
"sha256": "2f3449058cd4b3ab9e3ff8f541fa3c65ad2d94aabe20694cdec3a6e59c8a4fd6",
|
||||
"updaterProtocolVersion": 1,
|
||||
"updaterSha256": "066bb896726600ece9ce72697ce523c9a707cc9a621505931ba0892dec59d3c8"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user