;;; Parallel eager example — overlapping container startup latency
;;;
;;; db and cache are kicked off simultaneously with container-start-bg.
;;; The script continues immediately; it only blocks at container-join
;;; when a resolved value is actually needed.  Startup latency of db
;;; and cache overlaps rather than stacking.
;;;
;;; When to use:
;;;   Independent services whose startup can be overlapped, but you want
;;;   immediate imperative control rather than the declarative graph model.
;;;
;;; Compare with:
;;;   compose.reml                  — graph model: topo-sort, :parallel tiers,
;;;                                   cascade teardown — preferred for complex graphs
;;;   compose-eager-sequential.reml — same model, strictly serial
;;;
;;; Usage:
;;;   sudo -E pelagos compose up -f compose-eager-parallel.reml -p eager-par

;; ── Service declarations ──────────────────────────────────────────────────

(define-service svc-db "db"
  :image   "postgres:16"
  :network "app-net"
  :env     ("POSTGRES_PASSWORD" . "secret")
           ("POSTGRES_DB"       . "appdb")
           ("POSTGRES_USER"     . "app"))

(define-service svc-cache "cache"
  :image   "redis:7-alpine"
  :network "app-net")

(define-service svc-migrate "migrate"
  :image   "alpine:latest"
  :network "app-net"
  :command "/bin/sh" "-c"
           "echo \"migrating ${DATABASE_URL}\"; exit 0")

(define-service svc-app "app"
  :image   "alpine:latest"
  :network "app-net"
  :command "/bin/sh" "-c"
           "echo \"db=${DATABASE_URL} cache=${CACHE_URL}\"; sleep 30; echo done")

;; ── Parallel startup ──────────────────────────────────────────────────────

;; Kick off db and cache simultaneously — both start in background threads.
(define db-pending    (container-start-bg svc-db))
(define cache-pending (container-start-bg svc-cache))

;; Both are starting now.  Nothing blocks until container-join is called.

(define db    (container-join db-pending))
(define cache (container-join cache-pending))

(define db-url    (format "postgres://app:secret@~a/appdb" (container-ip db)))
(define cache-url (format "redis://~a:6379" (container-ip cache)))
(logf "db at ~a, cache at ~a" (container-ip db) (container-ip cache))

;; Run migrations against the now-ready db.
(define migrate (container-start svc-migrate
                  :env (list (cons "DATABASE_URL" db-url))))
(define exit-code (container-wait migrate))
(when (not (= exit-code 0))
  (container-stop cache)
  (container-stop db)
  (errorf "migrations failed with exit code ~a" exit-code))
(logf "migrations complete (exit ~a)" exit-code)

;; Start app with both URLs injected.
(define app (container-start svc-app
              :env (list (cons "DATABASE_URL" db-url)
                         (cons "CACHE_URL"    cache-url))))
(logf "app started at ~a" (container-ip app))

;; ── Wait and clean up ─────────────────────────────────────────────────────

(with-cleanup (lambda (result)
                (container-stop cache)
                (container-stop db)
                (if (ok? result)
                  (logf "app exited cleanly (code ~a)" (ok-value result))
                  (logf "app failed: ~a" (err-reason result))))
  (container-wait app))
(log "Done")
