feat: add workbench redis cache runtime config
This commit is contained in:
@@ -337,6 +337,7 @@
|
||||
"workbench": { "$ref": "#/$defs/workbenchUiPolicy" },
|
||||
"observability": { "$ref": "#/$defs/runtimeObservabilityPolicy" },
|
||||
"runtimeStore": { "$ref": "#/$defs/runtimeStore" },
|
||||
"workbenchRuntime": { "$ref": "#/$defs/workbenchRuntime" },
|
||||
"sourceRepo": { "type": "string" },
|
||||
"envReuseServices": {
|
||||
"type": "array",
|
||||
@@ -387,6 +388,123 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"workbenchRuntime": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cache": { "$ref": "#/$defs/workbenchRuntimeCache" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"workbenchRuntimeCache": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"redis": { "$ref": "#/$defs/workbenchRuntimeRedisCache" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"workbenchRuntimeRedisCache": {
|
||||
"type": "object",
|
||||
"required": ["enabled", "namespace", "serviceName", "port", "image", "resources", "memoryPolicy", "ttl", "limits", "timeouts", "metrics", "disableSwitchEnv", "unavailableBehavior"],
|
||||
"properties": {
|
||||
"enabled": { "type": "boolean" },
|
||||
"namespace": { "type": "string", "pattern": "^hwlab-v[0-9]{2,}$" },
|
||||
"serviceName": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 63,
|
||||
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$"
|
||||
},
|
||||
"port": { "type": "integer", "minimum": 1, "maximum": 65535 },
|
||||
"image": { "type": "string", "minLength": 1 },
|
||||
"resources": { "$ref": "#/$defs/workbenchRuntimeRedisResources" },
|
||||
"memoryPolicy": { "$ref": "#/$defs/workbenchRuntimeRedisMemoryPolicy" },
|
||||
"ttl": { "$ref": "#/$defs/workbenchRuntimeRedisTtl" },
|
||||
"limits": { "$ref": "#/$defs/workbenchRuntimeRedisLimits" },
|
||||
"timeouts": { "$ref": "#/$defs/workbenchRuntimeRedisTimeouts" },
|
||||
"metrics": { "$ref": "#/$defs/workbenchRuntimeRedisMetrics" },
|
||||
"disableSwitchEnv": { "type": "string", "pattern": "^[A-Z0-9_]+$" },
|
||||
"unavailableBehavior": { "$ref": "#/$defs/workbenchRuntimeRedisUnavailableBehavior" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"workbenchRuntimeRedisResources": {
|
||||
"type": "object",
|
||||
"required": ["requests", "limits"],
|
||||
"properties": {
|
||||
"requests": { "$ref": "#/$defs/workbenchRuntimeResourcePair" },
|
||||
"limits": { "$ref": "#/$defs/workbenchRuntimeResourcePair" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"workbenchRuntimeResourcePair": {
|
||||
"type": "object",
|
||||
"required": ["cpu", "memory"],
|
||||
"properties": {
|
||||
"cpu": { "type": "string", "minLength": 1 },
|
||||
"memory": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"workbenchRuntimeRedisMemoryPolicy": {
|
||||
"type": "object",
|
||||
"required": ["persistence", "maxMemory", "eviction"],
|
||||
"properties": {
|
||||
"persistence": { "type": "string", "enum": ["disabled"] },
|
||||
"maxMemory": { "type": "string", "minLength": 1 },
|
||||
"eviction": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"workbenchRuntimeRedisTtl": {
|
||||
"type": "object",
|
||||
"required": ["sessionsSummaryMs", "terminalTurnMs", "terminalTracePageMs", "runningPageMs"],
|
||||
"properties": {
|
||||
"sessionsSummaryMs": { "type": "integer", "minimum": 0 },
|
||||
"terminalTurnMs": { "type": "integer", "minimum": 0 },
|
||||
"terminalTracePageMs": { "type": "integer", "minimum": 0 },
|
||||
"runningPageMs": { "type": "integer", "minimum": 0 }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"workbenchRuntimeRedisLimits": {
|
||||
"type": "object",
|
||||
"required": ["maxKeyBytes", "maxPayloadBytes"],
|
||||
"properties": {
|
||||
"maxKeyBytes": { "type": "integer", "minimum": 1 },
|
||||
"maxPayloadBytes": { "type": "integer", "minimum": 1 }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"workbenchRuntimeRedisTimeouts": {
|
||||
"type": "object",
|
||||
"required": ["connectMs", "operationMs"],
|
||||
"properties": {
|
||||
"connectMs": { "type": "integer", "minimum": 1 },
|
||||
"operationMs": { "type": "integer", "minimum": 1 }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"workbenchRuntimeRedisMetrics": {
|
||||
"type": "object",
|
||||
"required": ["labels"],
|
||||
"properties": {
|
||||
"labels": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "type": "string", "minLength": 1 }
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"workbenchRuntimeRedisUnavailableBehavior": {
|
||||
"type": "object",
|
||||
"required": ["livenessFailure", "readMode", "fallbackStateSource"],
|
||||
"properties": {
|
||||
"livenessFailure": { "type": "boolean" },
|
||||
"readMode": { "type": "string", "enum": ["db-or-degraded"] },
|
||||
"fallbackStateSource": { "type": "boolean" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"workbenchUiPolicy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -306,6 +306,46 @@ lanes:
|
||||
queryRetryMaxAttempts: 5
|
||||
queryRetryInitialDelayMs: 250
|
||||
queryRetryMaxDelayMs: 5000
|
||||
workbenchRuntime:
|
||||
cache:
|
||||
redis:
|
||||
enabled: true
|
||||
namespace: hwlab-v03
|
||||
serviceName: hwlab-workbench-redis
|
||||
port: 6379
|
||||
image: redis:7.2-alpine
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 250m
|
||||
memory: 256Mi
|
||||
memoryPolicy:
|
||||
persistence: disabled
|
||||
maxMemory: 192mb
|
||||
eviction: allkeys-lru
|
||||
ttl:
|
||||
sessionsSummaryMs: 30000
|
||||
terminalTurnMs: 300000
|
||||
terminalTracePageMs: 300000
|
||||
runningPageMs: 5000
|
||||
limits:
|
||||
maxKeyBytes: 512
|
||||
maxPayloadBytes: 262144
|
||||
timeouts:
|
||||
connectMs: 200
|
||||
operationMs: 100
|
||||
metrics:
|
||||
labels:
|
||||
lane: v03
|
||||
service: hwlab-workbench-runtime
|
||||
cacheKeyClass: workbench-derived-read
|
||||
disableSwitchEnv: HWLAB_WORKBENCH_CACHE_REDIS_ENABLED
|
||||
unavailableBehavior:
|
||||
livenessFailure: false
|
||||
readMode: db-or-degraded
|
||||
fallbackStateSource: false
|
||||
envReuseServices:
|
||||
- hwlab-cloud-api
|
||||
- hwlab-workbench-runtime
|
||||
|
||||
@@ -938,7 +938,8 @@ function deployServicesForProfile(deploy, profile) {
|
||||
copy.env = mergeEnvMaps(
|
||||
copy.env,
|
||||
serviceDeclarationEnvForProfile(deploy, profile, service.serviceId),
|
||||
runtimeStoreEnvForProfile(deploy, profile, service.serviceId)
|
||||
runtimeStoreEnvForProfile(deploy, profile, service.serviceId),
|
||||
workbenchRuntimeRedisEnvForProfile(deploy, profile, service.serviceId)
|
||||
);
|
||||
return [service.serviceId, copy];
|
||||
}));
|
||||
@@ -950,7 +951,8 @@ function deployServicesForProfile(deploy, profile) {
|
||||
serviceId: override.serviceId,
|
||||
env: mergeEnvMaps(
|
||||
serviceDeclarationEnvForProfile(deploy, profile, override.serviceId),
|
||||
runtimeStoreEnvForProfile(deploy, profile, override.serviceId)
|
||||
runtimeStoreEnvForProfile(deploy, profile, override.serviceId),
|
||||
workbenchRuntimeRedisEnvForProfile(deploy, profile, override.serviceId)
|
||||
)
|
||||
};
|
||||
services.set(override.serviceId, {
|
||||
@@ -1006,6 +1008,47 @@ function runtimeStoreEnvForProfile(deploy, profile, serviceId) {
|
||||
return env;
|
||||
}
|
||||
|
||||
function workbenchRuntimeRedisEnvForProfile(deploy, profile, serviceId) {
|
||||
if (!isRuntimeLane(profile) || serviceId !== "hwlab-workbench-runtime") return {};
|
||||
const redis = runtimeLaneConfig(deploy, profile)?.workbenchRuntime?.cache?.redis;
|
||||
if (!redis || typeof redis !== "object" || Array.isArray(redis)) return {};
|
||||
const env = {
|
||||
HWLAB_WORKBENCH_CACHE_REDIS_ENABLED: booleanEnv(Boolean(redis.enabled))
|
||||
};
|
||||
const namespace = typeof redis.namespace === "string" ? redis.namespace.trim() : "";
|
||||
const serviceName = typeof redis.serviceName === "string" ? redis.serviceName.trim() : "";
|
||||
const port = Number.isInteger(redis.port) ? redis.port : null;
|
||||
if (namespace) env.HWLAB_WORKBENCH_CACHE_REDIS_NAMESPACE = namespace;
|
||||
if (serviceName) env.HWLAB_WORKBENCH_CACHE_REDIS_SERVICE_NAME = serviceName;
|
||||
if (port !== null) env.HWLAB_WORKBENCH_CACHE_REDIS_PORT = String(port);
|
||||
if (namespace && serviceName && port !== null) {
|
||||
env.HWLAB_WORKBENCH_CACHE_REDIS_URL = `redis://${serviceName}.${namespace}.svc.cluster.local:${port}/0`;
|
||||
}
|
||||
const ttl = redis.ttl ?? {};
|
||||
if (Number.isInteger(ttl.sessionsSummaryMs)) env.HWLAB_WORKBENCH_CACHE_REDIS_TTL_SESSIONS_SUMMARY_MS = String(ttl.sessionsSummaryMs);
|
||||
if (Number.isInteger(ttl.terminalTurnMs)) env.HWLAB_WORKBENCH_CACHE_REDIS_TTL_TERMINAL_TURN_MS = String(ttl.terminalTurnMs);
|
||||
if (Number.isInteger(ttl.terminalTracePageMs)) env.HWLAB_WORKBENCH_CACHE_REDIS_TTL_TERMINAL_TRACE_PAGE_MS = String(ttl.terminalTracePageMs);
|
||||
if (Number.isInteger(ttl.runningPageMs)) env.HWLAB_WORKBENCH_CACHE_REDIS_TTL_RUNNING_PAGE_MS = String(ttl.runningPageMs);
|
||||
const limits = redis.limits ?? {};
|
||||
if (Number.isInteger(limits.maxKeyBytes)) env.HWLAB_WORKBENCH_CACHE_REDIS_MAX_KEY_BYTES = String(limits.maxKeyBytes);
|
||||
if (Number.isInteger(limits.maxPayloadBytes)) env.HWLAB_WORKBENCH_CACHE_REDIS_MAX_PAYLOAD_BYTES = String(limits.maxPayloadBytes);
|
||||
const timeouts = redis.timeouts ?? {};
|
||||
if (Number.isInteger(timeouts.connectMs)) env.HWLAB_WORKBENCH_CACHE_REDIS_CONNECT_TIMEOUT_MS = String(timeouts.connectMs);
|
||||
if (Number.isInteger(timeouts.operationMs)) env.HWLAB_WORKBENCH_CACHE_REDIS_OPERATION_TIMEOUT_MS = String(timeouts.operationMs);
|
||||
const labels = redis.metrics?.labels;
|
||||
if (labels && typeof labels === "object" && !Array.isArray(labels)) {
|
||||
env.HWLAB_WORKBENCH_CACHE_REDIS_METRICS_LABELS = JSON.stringify(labels);
|
||||
}
|
||||
const behavior = redis.unavailableBehavior ?? {};
|
||||
if (typeof behavior.livenessFailure === "boolean") env.HWLAB_WORKBENCH_CACHE_REDIS_UNAVAILABLE_LIVENESS_FAILURE = booleanEnv(behavior.livenessFailure);
|
||||
if (typeof behavior.readMode === "string") env.HWLAB_WORKBENCH_CACHE_REDIS_UNAVAILABLE_READ_MODE = behavior.readMode;
|
||||
if (typeof behavior.fallbackStateSource === "boolean") env.HWLAB_WORKBENCH_CACHE_REDIS_UNAVAILABLE_FALLBACK_STATE_SOURCE = booleanEnv(behavior.fallbackStateSource);
|
||||
if (typeof redis.disableSwitchEnv === "string" && redis.disableSwitchEnv.trim()) {
|
||||
env[redis.disableSwitchEnv.trim()] = booleanEnv(Boolean(redis.enabled));
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
function booleanEnv(value) {
|
||||
return value ? "1" : "0";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user