Files
sless/sqs-operator/patch_v0113_recreate.py
T
Naeel d1d1bffd7c v0.1.13: Strategy Recreate + preStop + liveness fix (ERR-SQS-06 root cause)
- ensureDeployment: Strategy Recreate (not RollingUpdate) — prevents H2 file lock
  when two pods mount same PVC simultaneously during rollout restart
- preStop: sleep 3 — graceful H2 shutdown before SIGTERM
- livenessProbe timeoutSeconds: 3 — prevents false positive on GC pause
- terminationGracePeriodSeconds: 15
- doc: thinking log, ERR-SQS-06, progress.md, architecture SVG schema
2026-04-09 07:49:11 +03:00

79 lines
3.1 KiB
Python

#!/usr/bin/env python3
# 2026-04-09 — v0.1.13: Strategy Recreate + preStop hook + liveness timeout fix
import sys
FILE = "/home/naeel/terra/sless/sqs-operator/internal/controller/queueservice_controller.go"
with open(FILE, "r") as f:
code = f.read()
# === PATCH 1: Strategy Recreate ===
old_1 = "\t\t\t},\n\t\t\tTemplate: corev1.PodTemplateSpec{"
new_1 = """\t\t\t},
\t\t\t// Recreate: старый pod убивается ДО создания нового.
\t\t\t// H2 MVStore держит FileChannel.lock() на /data/elasticmq.mv.db —
\t\t\t// при RollingUpdate два pod лезут в один PVC одновременно = ERR-SQS-06.
\t\t\tStrategy: appsv1.DeploymentStrategy{
\t\t\t\tType: appsv1.RecreateDeploymentStrategyType,
\t\t\t},
\t\t\tTemplate: corev1.PodTemplateSpec{"""
count = code.count(old_1)
if count != 1:
print(f"FAIL patch 1: found {count} matches for Selector close + Template")
sys.exit(1)
code = code.replace(old_1, new_1, 1)
print("PATCH 1 OK: Strategy Recreate")
# === PATCH 2: terminationGracePeriodSeconds ===
old_2 = "\t\t\t\t\tSecurityContext: &corev1.PodSecurityContext{\n\t\t\t\t\t\tFSGroup: func() *int64 { v := int64(999); return &v }(),\n\t\t\t\t\t},\n\t\t\t\t\tContainers: func() []corev1.Container {"
new_2 = """\t\t\t\t\tSecurityContext: &corev1.PodSecurityContext{
\t\t\t\t\t\tFSGroup: func() *int64 { v := int64(999); return &v }(),
\t\t\t\t\t},
\t\t\t\t\t// 15 сек — достаточно для graceful shutdown JVM + H2 fsync.
\t\t\t\t\tTerminationGracePeriodSeconds: func() *int64 { v := int64(15); return &v }(),
\t\t\t\t\tContainers: func() []corev1.Container {"""
count = code.count(old_2)
if count != 1:
print(f"FAIL patch 2: found {count} matches for SecurityContext + Containers")
sys.exit(1)
code = code.replace(old_2, new_2, 1)
print("PATCH 2 OK: terminationGracePeriodSeconds 15")
# === PATCH 3: LivenessProbe TimeoutSeconds=3 + preStop hook ===
old_3 = "\t\t\t\t\t\t\t\t\tInitialDelaySeconds: 5,\n\t\t\t\t\t\t\t\t\tPeriodSeconds: 10,\n\t\t\t\t\t\t\t\t\tFailureThreshold: 5,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}"
new_3 = """\t\t\t\t\t\t\t\t\tInitialDelaySeconds: 5,
\t\t\t\t\t\t\t\t\tPeriodSeconds: 10,
\t\t\t\t\t\t\t\t\tTimeoutSeconds: 3,
\t\t\t\t\t\t\t\t\tFailureThreshold: 5,
\t\t\t\t\t\t\t\t},
\t\t\t\t\t\t\t\t// preStop: 3 сек на graceful shutdown H2 перед SIGTERM.
\t\t\t\t\t\t\t\tLifecycle: &corev1.Lifecycle{
\t\t\t\t\t\t\t\t\tPreStop: &corev1.LifecycleHandler{
\t\t\t\t\t\t\t\t\t\tExec: &corev1.ExecAction{
\t\t\t\t\t\t\t\t\t\t\tCommand: []string{"sh", "-c", "sleep 3"},
\t\t\t\t\t\t\t\t\t\t},
\t\t\t\t\t\t\t\t\t},
\t\t\t\t\t\t\t\t},
\t\t\t\t\t\t\t},
\t\t\t\t\t\t}"""
count = code.count(old_3)
if count != 1:
print(f"FAIL patch 3: found {count} matches for LivenessProbe block")
sys.exit(1)
code = code.replace(old_3, new_3, 1)
print("PATCH 3 OK: timeoutSeconds=3 + preStop sleep 3")
# === PATCH 4: version ===
if "0.1.12" in code:
code = code.replace("0.1.12", "0.1.13")
print("PATCH 4 OK: version -> 0.1.13")
else:
print("PATCH 4 SKIP: version string not found")
with open(FILE, "w") as f:
f.write(code)
print("\nAll patches applied.")