# tidalDB standalone server as a StatefulSet.
#
# WHY A STATEFULSET (not a Deployment): tidalDB is single-node-first and
# embeddable — the server wraps ONE engine instance whose state (WAL +
# checkpoints + indexes) lives on a durable data dir. It scales VERTICALLY
# (bigger node), not by adding replicas. `replicas: 1` is intentional and load-
# bearing: there is no shared-storage multi-writer mode. The experimental
# multi-region `cluster` subcommand runs every region in ONE process (no host
# isolation — NOT production HA, tracked as m8p10), so it does not change this.
# See docs/runbooks/kubernetes.md and docs/runbooks/cluster.md.
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: tidaldb
  namespace: tidaldb
  labels:
    app.kubernetes.io/name: tidaldb
    app.kubernetes.io/component: server
spec:
  serviceName: tidaldb        # the headless Service in service.yaml — stable network id
  replicas: 1                 # single-node-first: scale up, not out (see header)
  selector:
    matchLabels:
      app.kubernetes.io/name: tidaldb
  template:
    metadata:
      labels:
        app.kubernetes.io/name: tidaldb
        app.kubernetes.io/component: server
      annotations:
        # Plain-Prometheus scrape hints (the PodMonitor/ServiceMonitor in
        # servicemonitor.yaml is the Operator-native alternative). Metrics are
        # unauthenticated — keep :9091 cluster-internal (see ops/monitoring.md).
        prometheus.io/scrape: "true"
        prometheus.io/port: "9091"
        prometheus.io/path: "/metrics"
    spec:
      # SIGTERM flips readiness to 503 (pod leaves Endpoints), drains in-flight
      # requests, then checkpoints + fsyncs the WAL before exit. Give that room.
      terminationGracePeriodSeconds: 60
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001        # the `tidal` user baked into docker/deploy/Dockerfile
        runAsGroup: 10001
        fsGroup: 10001          # makes the mounted PVC group-writable by the runtime user
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: tidaldb
          # For a real cluster, replace with your registry image pinned by digest
          # (e.g. registry.example.com/tidaldb@sha256:...) and set
          # imagePullPolicy: IfNotPresent. `tidaldb:deploy` is the local image
          # built from docker/deploy/Dockerfile and loaded via `kind load`.
          image: tidaldb:deploy
          imagePullPolicy: IfNotPresent
          # ENTRYPOINT is the bare binary; these args override the image CMD so
          # the schema comes from the mounted ConfigMap, not the baked default.
          args:
            - standalone
            - --listen
            - 0.0.0.0:9400
            - --schema
            - /etc/tidaldb/schema/schema.yaml
            - --data-dir
            - /data
            - --metrics
            - 0.0.0.0:9091
          env:
            - name: TIDAL_API_KEY
              valueFrom:
                secretKeyRef:
                  name: tidaldb-api-key
                  key: api-key
            - name: TIDAL_SERVER_LOG
              value: info
          ports:
            - name: http
              containerPort: 9400
            - name: metrics
              containerPort: 9091
          # Three distinct probes map to the three health endpoints:
          #  - /health/startup : always 200 once the HTTP listener is up
          #  - /health/live    : always 200 while the process is alive
          #  - /health         : 200 ready / 503 while draining on SIGTERM
          # All are unauthenticated by design, so the probes need no token.
          startupProbe:
            httpGet:
              path: /health/startup
              port: http
            periodSeconds: 5
            failureThreshold: 60     # up to ~5 min for large-DB WAL replay / index load (capacity-planning.md)
          livenessProbe:
            httpGet:
              path: /health/live
              port: http
            periodSeconds: 10
            timeoutSeconds: 3
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /health          # 503 during drain -> removed from Service Endpoints
              port: http
            periodSeconds: 10
            timeoutSeconds: 3
            failureThreshold: 3
          resources:
            requests:
              cpu: "250m"
              memory: 256Mi
            limits:
              cpu: "2"
              memory: 2Gi           # size from docs/ops/capacity-planning.md for your item/embedding count
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true   # the server only writes /data (PVC) and /tmp (emptyDir)
            capabilities:
              drop: ["ALL"]
          volumeMounts:
            - name: data
              mountPath: /data
            - name: schema
              mountPath: /etc/tidaldb/schema
              readOnly: true
            - name: tmp
              mountPath: /tmp
      volumes:
        - name: schema
          configMap:
            name: tidaldb-schema
        - name: tmp
          emptyDir: {}
  volumeClaimTemplates:
    - metadata:
        name: data
        labels:
          app.kubernetes.io/name: tidaldb
      spec:
        accessModes: ["ReadWriteOnce"]
        # storageClassName: ""   # uncomment + set to pin a class; omitted = cluster default
        resources:
          requests:
            storage: 10Gi
