| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- package grpcserver
- import (
- "context"
- "fmt"
- "net"
- "time"
- pbv1 "git3.techno-world.net/lrosales/broad-announce/gen/go/broadannounce/v1"
- "git3.techno-world.net/lrosales/broad-announce/internal/observability"
- "git3.techno-world.net/lrosales/broad-announce/internal/pipeline"
- "google.golang.org/grpc"
- "google.golang.org/grpc/keepalive"
- )
- // Config for the gRPC server.
- type Config struct {
- Addr string // default ":9090"
- MaxInflight int // max in-flight per stream; default 256
- }
- // IngestdDeps is what ingestd's main.go passes to NewForIngestd.
- // Using an interface avoids an import cycle (internal/grpcserver cannot import cmd/ingestd).
- type IngestdDeps interface {
- GetLogger() interface{ Info(msg string, args ...any) }
- GetMetrics() *observability.IngestdMetrics
- GetPipeline() pipeline.Deps
- }
- // NewForIngestd is the constructor ingestd's main.go calls.
- // It registers the Ingest service on a grpc.Server but does not start listening.
- func NewForIngestd(cfg Config, deps IngestdDeps) (*grpc.Server, error) {
- if cfg.Addr == "" {
- cfg.Addr = ":9090"
- }
- if cfg.MaxInflight == 0 {
- cfg.MaxInflight = 256
- }
- logger := deps.GetLogger()
- m := deps.GetMetrics()
- // Prime the histogram buckets with a zero observation so Prometheus
- // registers them immediately (avoids missing bucket labels on first scrape).
- m.GRPCAckLatency.WithLabelValues("_init").Observe(0)
- ka := keepalive.ServerParameters{
- Time: 30 * time.Second,
- Timeout: 10 * time.Second,
- }
- enforcement := keepalive.EnforcementPolicy{
- MinTime: 5 * time.Second,
- PermitWithoutStream: false,
- }
- grpcOpts := []grpc.ServerOption{
- grpc.KeepaliveParams(ka),
- grpc.KeepaliveEnforcementPolicy(enforcement),
- grpc.WriteBufferSize(256 * 1024),
- grpc.ReadBufferSize(32 * 1024),
- // TODO(m11): grpc.Creds(tlsCredentials()) — enable TLS once certificates are available
- }
- srv := grpc.NewServer(grpcOpts...)
- pbv1.RegisterIngestServer(srv, &Server{
- Dep: deps.GetPipeline(),
- MaxInflight: cfg.MaxInflight,
- Logger: logger,
- })
- logger.Info("grpc server configured", "addr", cfg.Addr, "max_inflight", cfg.MaxInflight)
- return srv, nil
- }
- // Serve starts a TCP listener on addr and blocks serving.
- // On ctx cancellation, it initiates a graceful shutdown (drains for up to 15s
- // then hard-stops). The caller should run Serve in a goroutine.
- func Serve(ctx context.Context, cfg Config, deps IngestdDeps) error {
- srv, err := NewForIngestd(cfg, deps)
- if err != nil {
- return fmt.Errorf("grpcserver.NewForIngestd: %w", err)
- }
- ln, err := net.Listen("tcp", cfg.Addr)
- if err != nil {
- return fmt.Errorf("grpc listen %s: %w", cfg.Addr, err)
- }
- errCh := make(chan error, 1)
- go func() { errCh <- srv.Serve(ln) }()
- select {
- case <-ctx.Done():
- done := make(chan struct{})
- go func() {
- srv.GracefulStop()
- close(done)
- }()
- select {
- case <-done:
- return nil
- case <-time.After(15 * time.Second):
- srv.Stop()
- return ctx.Err()
- }
- case err := <-errCh:
- return err
- }
- }
|