main.go raw

   1  // orly-sync-relaygroup is a standalone gRPC relay group service for ORLY.
   2  // It provides relay group configuration discovery from Kind 39105 events.
   3  package main
   4  
   5  import (
   6  	"context"
   7  	"net"
   8  	"os"
   9  	"os/signal"
  10  	"syscall"
  11  	"time"
  12  
  13  	"google.golang.org/grpc"
  14  	"google.golang.org/grpc/reflection"
  15  	"next.orly.dev/pkg/lol"
  16  	"next.orly.dev/pkg/lol/chk"
  17  	"next.orly.dev/pkg/lol/log"
  18  
  19  	"next.orly.dev/pkg/database"
  20  	databasegrpc "next.orly.dev/pkg/database/grpc"
  21  	"next.orly.dev/pkg/sync/relaygroup"
  22  	relaygroupv1 "next.orly.dev/pkg/proto/orlysync/relaygroup/v1"
  23  )
  24  
  25  func main() {
  26  	cfg := loadConfig()
  27  
  28  	// Set log level
  29  	lol.SetLogLevel(cfg.LogLevel)
  30  	log.I.F("orly-sync-relaygroup starting with log level: %s", cfg.LogLevel)
  31  
  32  	ctx, cancel := context.WithCancel(context.Background())
  33  	defer cancel()
  34  
  35  	// Initialize database connection
  36  	var db database.Database
  37  	var dbCloser func()
  38  
  39  	if cfg.DBType == "grpc" {
  40  		log.I.F("connecting to gRPC database server at %s", cfg.GRPCDBServer)
  41  		dbClient, err := databasegrpc.New(ctx, &databasegrpc.ClientConfig{
  42  			ServerAddress:  cfg.GRPCDBServer,
  43  			ConnectTimeout: 30 * time.Second,
  44  		})
  45  		if chk.E(err) {
  46  			log.E.F("failed to connect to database server: %v", err)
  47  			os.Exit(1)
  48  		}
  49  		db = dbClient
  50  		dbCloser = func() { dbClient.Close() }
  51  	} else {
  52  		log.E.F("badger mode not yet implemented for sync-relaygroup")
  53  		os.Exit(1)
  54  	}
  55  
  56  	// Wait for database to be ready
  57  	log.I.F("waiting for database to be ready...")
  58  	<-db.Ready()
  59  	log.I.F("database ready")
  60  
  61  	// Create relay group manager configuration
  62  	relaygroupCfg := &relaygroup.ManagerConfig{
  63  		AdminNpubs: cfg.AdminNpubs,
  64  	}
  65  
  66  	log.I.F("initializing relay group manager with %d admins", len(cfg.AdminNpubs))
  67  
  68  	// Create gRPC server
  69  	grpcServer := grpc.NewServer(
  70  		grpc.MaxRecvMsgSize(16<<20), // 16MB
  71  		grpc.MaxSendMsgSize(16<<20), // 16MB
  72  	)
  73  
  74  	// Register relay group service
  75  	// TODO: Create and register service once server implementation is done
  76  	// service := relaygroup.NewService(relaygroupMgr, cfg)
  77  	// relaygroupv1.RegisterRelayGroupServiceServer(grpcServer, service)
  78  	_ = relaygroupCfg
  79  	_ = db
  80  	_ = relaygroupv1.RelayGroupService_ServiceDesc
  81  
  82  	// Register reflection for debugging with grpcurl
  83  	reflection.Register(grpcServer)
  84  
  85  	// Start listening
  86  	lis, err := net.Listen("tcp", cfg.Listen)
  87  	if chk.E(err) {
  88  		log.E.F("failed to listen on %s: %v", cfg.Listen, err)
  89  		os.Exit(1)
  90  	}
  91  	log.I.F("gRPC server listening on %s", cfg.Listen)
  92  
  93  	// Handle graceful shutdown
  94  	go func() {
  95  		sigs := make(chan os.Signal, 1)
  96  		signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT)
  97  		sig := <-sigs
  98  		log.I.F("received signal %v, shutting down...", sig)
  99  
 100  		cancel()
 101  
 102  		stopped := make(chan struct{})
 103  		go func() {
 104  			grpcServer.GracefulStop()
 105  			close(stopped)
 106  		}()
 107  
 108  		select {
 109  		case <-stopped:
 110  			log.I.F("gRPC server stopped gracefully")
 111  		case <-time.After(5 * time.Second):
 112  			log.W.F("gRPC graceful stop timed out, forcing stop")
 113  			grpcServer.Stop()
 114  		}
 115  
 116  		if dbCloser != nil {
 117  			log.I.F("closing database connection...")
 118  			dbCloser()
 119  		}
 120  		log.I.F("shutdown complete")
 121  	}()
 122  
 123  	// Serve gRPC
 124  	if err := grpcServer.Serve(lis); err != nil {
 125  		log.E.F("gRPC server error: %v", err)
 126  	}
 127  }
 128