molecule-core/workspace-server/internal/db/redis.go
Hongming Wang d8026347e5 chore: open-source restructure — rename dirs, remove internal files, scrub secrets
Renames:
- platform/ → workspace-server/ (Go module path stays as "platform" for
  external dep compat — will update after plugin module republish)
- workspace-template/ → workspace/

Removed (moved to separate repos or deleted):
- PLAN.md — internal roadmap (move to private project board)
- HANDOFF.md, AGENTS.md — one-time internal session docs
- .claude/ — gitignored entirely (local agent config)
- infra/cloudflare-worker/ → Molecule-AI/molecule-tenant-proxy
- org-templates/molecule-dev/ → standalone template repo
- .mcp-eval/ → molecule-mcp-server repo
- test-results/ — ephemeral, gitignored

Security scrubbing:
- Cloudflare account/zone/KV IDs → placeholders
- Real EC2 IPs → <EC2_IP> in all docs
- CF token prefix, Neon project ID, Fly app names → redacted
- Langfuse dev credentials → parameterized
- Personal runner username/machine name → generic

Community files:
- CONTRIBUTING.md — build, test, branch conventions
- CODE_OF_CONDUCT.md — Contributor Covenant 2.1

All Dockerfiles, CI workflows, docker-compose, railway.toml, render.yaml,
README, CLAUDE.md updated for new directory names.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 00:24:44 -07:00

95 lines
3.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package db
import (
"context"
"fmt"
"log"
"time"
"github.com/redis/go-redis/v9"
)
var RDB *redis.Client
func InitRedis(redisURL string) error {
opts, err := redis.ParseURL(redisURL)
if err != nil {
return fmt.Errorf("parse redis url: %w", err)
}
RDB = redis.NewClient(opts)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := RDB.Ping(ctx).Err(); err != nil {
return fmt.Errorf("ping redis: %w", err)
}
log.Println("Connected to Redis")
return nil
}
// LivenessTTL is the TTL for the workspace liveness key in Redis.
// Must be > heartbeat interval × (max acceptable missed heartbeats).
// Workspace heartbeat loop fires every 30s; a busy Claude Code / Opus
// synthesis can starve the asyncio scheduler for 60-120s, so a 60s TTL
// triggered false-positive "unreachable — restart" cycles on busy
// leaders every ~30 minutes (see README in this package + the commit
// message). 180s allows up to ~5 missed heartbeats before we conclude
// the container is actually dead, which still cleanly detects real
// crashes (the a2a_proxy reactive IsRunning() check catches those on
// the first failed forward, independent of TTL).
const LivenessTTL = 180 * time.Second
// SetOnline sets the workspace liveness key with the LivenessTTL.
func SetOnline(ctx context.Context, workspaceID string) error {
key := fmt.Sprintf("ws:%s", workspaceID)
return RDB.Set(ctx, key, "online", LivenessTTL).Err()
}
// RefreshTTL refreshes the liveness TTL for a workspace.
func RefreshTTL(ctx context.Context, workspaceID string) error {
key := fmt.Sprintf("ws:%s", workspaceID)
return RDB.Expire(ctx, key, LivenessTTL).Err()
}
// CacheURL caches a workspace URL for fast resolution.
func CacheURL(ctx context.Context, workspaceID, url string) error {
key := fmt.Sprintf("ws:%s:url", workspaceID)
return RDB.Set(ctx, key, url, 5*time.Minute).Err()
}
// GetCachedURL gets a cached workspace URL.
func GetCachedURL(ctx context.Context, workspaceID string) (string, error) {
key := fmt.Sprintf("ws:%s:url", workspaceID)
return RDB.Get(ctx, key).Result()
}
// CacheInternalURL caches the Docker-internal URL for workspace-to-workspace discovery.
func CacheInternalURL(ctx context.Context, workspaceID, url string) error {
key := fmt.Sprintf("ws:%s:internal_url", workspaceID)
return RDB.Set(ctx, key, url, 5*time.Minute).Err()
}
// GetCachedInternalURL gets the Docker-internal URL for a workspace.
func GetCachedInternalURL(ctx context.Context, workspaceID string) (string, error) {
key := fmt.Sprintf("ws:%s:internal_url", workspaceID)
return RDB.Get(ctx, key).Result()
}
// ClearWorkspaceKeys removes all Redis keys for a workspace (liveness, URL cache, internal URL cache).
func ClearWorkspaceKeys(ctx context.Context, workspaceID string) {
for _, suffix := range []string{"", ":url", ":internal_url"} {
RDB.Del(ctx, fmt.Sprintf("ws:%s%s", workspaceID, suffix))
}
}
// IsOnline checks if a workspace is online.
func IsOnline(ctx context.Context, workspaceID string) (bool, error) {
key := fmt.Sprintf("ws:%s", workspaceID)
val, err := RDB.Exists(ctx, key).Result()
if err != nil {
return false, err
}
return val > 0, nil
}