Add HTTP /v1 MVC, TCP send path, groups with server-side member-avatar collage, object-storage upload, and friends/favorites around the architecture-v3 layout. Co-authored-by: Cursor <cursoragent@cursor.com>
96 lines
2.2 KiB
Go
96 lines
2.2 KiB
Go
package bootstrap
|
|
|
|
import (
|
|
"context"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"spark-server/internal/infrastructure/mysql"
|
|
redisx "spark-server/internal/infrastructure/redis"
|
|
tcp "spark-server/internal/transport/tcp"
|
|
)
|
|
|
|
const (
|
|
depOK = "ok"
|
|
depSkipped = "skipped"
|
|
depDown = "down"
|
|
depClosed = "closed"
|
|
)
|
|
|
|
// ProbeReport is /readyz data. LB should use HTTP 503 when Ready is false.
|
|
type ProbeReport struct {
|
|
Status string `json:"status"`
|
|
Accepting bool `json:"accepting"`
|
|
MySQL string `json:"mysql"`
|
|
Redis string `json:"redis"`
|
|
ObjectStorage string `json:"object_storage"`
|
|
TaskPool string `json:"taskpool"`
|
|
}
|
|
|
|
// Probe implements httpx.Health for liveness/readiness.
|
|
type Probe struct {
|
|
mysql *mysql.DB
|
|
redis *redisx.Client
|
|
objects pinger
|
|
pool tcp.TaskPool
|
|
accepting *atomic.Bool
|
|
requireDeps bool
|
|
}
|
|
|
|
func (p *Probe) Ready(ctx context.Context) (bool, any) {
|
|
if p == nil {
|
|
return true, ProbeReport{Status: depOK, Accepting: true, MySQL: depSkipped, Redis: depSkipped, ObjectStorage: depSkipped, TaskPool: depOK}
|
|
}
|
|
ctx, cancel := context.WithTimeout(ctx, time.Second)
|
|
defer cancel()
|
|
rep := ProbeReport{
|
|
Accepting: p.accepting != nil && p.accepting.Load(),
|
|
MySQL: p.checkStore(ctx, p.mysql),
|
|
Redis: p.checkStore(ctx, p.redis),
|
|
ObjectStorage: p.checkOptional(ctx, p.objects),
|
|
TaskPool: depOK,
|
|
}
|
|
ready := rep.Accepting
|
|
if p.pool != nil && p.pool.Closed() {
|
|
rep.TaskPool = depClosed
|
|
ready = false
|
|
}
|
|
if rep.MySQL == depDown || rep.Redis == depDown || rep.ObjectStorage == depDown {
|
|
ready = false
|
|
}
|
|
if ready {
|
|
rep.Status = depOK
|
|
} else {
|
|
rep.Status = "not_ready"
|
|
}
|
|
return ready, rep
|
|
}
|
|
|
|
type pinger interface {
|
|
Ready() bool
|
|
Ping(ctx context.Context) error
|
|
}
|
|
|
|
func (p *Probe) checkStore(ctx context.Context, s pinger) string {
|
|
if s == nil || !s.Ready() {
|
|
if p.requireDeps {
|
|
return depDown
|
|
}
|
|
return depSkipped
|
|
}
|
|
if err := s.Ping(ctx); err != nil {
|
|
return depDown
|
|
}
|
|
return depOK
|
|
}
|
|
|
|
func (p *Probe) checkOptional(ctx context.Context, s pinger) string {
|
|
if s == nil || !s.Ready() {
|
|
return depSkipped
|
|
}
|
|
if err := s.Ping(ctx); err != nil {
|
|
return depDown
|
|
}
|
|
return depOK
|
|
}
|