main.go raw

   1  // orly-db-neo4j is a standalone gRPC database server using the Neo4j backend.
   2  package main
   3  
   4  import (
   5  	"context"
   6  	"os"
   7  	"time"
   8  
   9  	"go-simpler.org/env"
  10  	"next.orly.dev/pkg/lol"
  11  	"next.orly.dev/pkg/lol/chk"
  12  	"next.orly.dev/pkg/lol/log"
  13  
  14  	"next.orly.dev/pkg/database"
  15  	"next.orly.dev/pkg/database/server"
  16  
  17  	// Import neo4j to register the factory
  18  	_ "next.orly.dev/pkg/neo4j"
  19  )
  20  
  21  // Config holds the database server configuration.
  22  type Config struct {
  23  	// Listen is the gRPC server listen address
  24  	Listen string `env:"ORLY_DB_LISTEN" default:"127.0.0.1:50051" usage:"gRPC server listen address"`
  25  
  26  	// LogLevel is the logging level
  27  	LogLevel string `env:"ORLY_DB_LOG_LEVEL" default:"info" usage:"log level (trace, debug, info, warn, error)"`
  28  
  29  	// DataDir is required by the database layer for metadata storage
  30  	DataDir string `env:"ORLY_DATA_DIR" default:"/tmp/orly-neo4j" usage:"data directory for metadata"`
  31  
  32  	// Neo4j configuration
  33  	Neo4jURI      string `env:"ORLY_NEO4J_URI" default:"bolt://localhost:7687" usage:"Neo4j connection URI"`
  34  	Neo4jUser     string `env:"ORLY_NEO4J_USER" default:"neo4j" usage:"Neo4j username"`
  35  	Neo4jPassword string `env:"ORLY_NEO4J_PASSWORD" usage:"Neo4j password"`
  36  
  37  	// Neo4j driver tuning
  38  	Neo4jMaxConnPoolSize   int `env:"ORLY_NEO4J_MAX_CONN_POOL" default:"25" usage:"max connection pool size"`
  39  	Neo4jFetchSize         int `env:"ORLY_NEO4J_FETCH_SIZE" default:"1000" usage:"max records per fetch batch"`
  40  	Neo4jMaxTxRetrySeconds int `env:"ORLY_NEO4J_MAX_TX_RETRY_SEC" default:"30" usage:"max transaction retry time"`
  41  	Neo4jQueryResultLimit  int `env:"ORLY_NEO4J_QUERY_RESULT_LIMIT" default:"10000" usage:"max results per query (0=unlimited)"`
  42  
  43  	// Query cache configuration (for the gRPC server)
  44  	QueryCacheSizeMB   int           `env:"ORLY_DB_QUERY_CACHE_SIZE_MB" default:"256" usage:"query cache size in MB"`
  45  	QueryCacheMaxAge   time.Duration `env:"ORLY_DB_QUERY_CACHE_MAX_AGE" default:"5m" usage:"query cache max age"`
  46  	QueryCacheDisabled bool          `env:"ORLY_DB_QUERY_CACHE_DISABLED" default:"false" usage:"disable query cache"`
  47  
  48  	// gRPC server configuration
  49  	StreamBatchSize int `env:"ORLY_DB_STREAM_BATCH_SIZE" default:"100" usage:"events per stream batch"`
  50  }
  51  
  52  func main() {
  53  	cfg := loadConfig()
  54  
  55  	// Set log level
  56  	lol.SetLogLevel(cfg.LogLevel)
  57  	log.I.F("orly-db-neo4j starting with log level: %s", cfg.LogLevel)
  58  
  59  	ctx, cancel := context.WithCancel(context.Background())
  60  	defer cancel()
  61  
  62  	// Create database configuration
  63  	dbCfg := &database.DatabaseConfig{
  64  		DataDir:              cfg.DataDir,
  65  		LogLevel:             cfg.LogLevel,
  66  		Neo4jURI:             cfg.Neo4jURI,
  67  		Neo4jUser:            cfg.Neo4jUser,
  68  		Neo4jPassword:        cfg.Neo4jPassword,
  69  		Neo4jMaxConnPoolSize: cfg.Neo4jMaxConnPoolSize,
  70  		Neo4jFetchSize:       cfg.Neo4jFetchSize,
  71  		Neo4jMaxTxRetrySeconds: cfg.Neo4jMaxTxRetrySeconds,
  72  		Neo4jQueryResultLimit:  cfg.Neo4jQueryResultLimit,
  73  		QueryCacheSizeMB:     cfg.QueryCacheSizeMB,
  74  		QueryCacheMaxAge:     cfg.QueryCacheMaxAge,
  75  		QueryCacheDisabled:   cfg.QueryCacheDisabled,
  76  	}
  77  
  78  	// Initialize Neo4j database via factory
  79  	log.I.F("connecting to Neo4j at %s", cfg.Neo4jURI)
  80  	db, err := database.NewDatabaseWithConfig(ctx, cancel, "neo4j", dbCfg)
  81  	if chk.E(err) {
  82  		log.E.F("failed to initialize Neo4j database: %v", err)
  83  		os.Exit(1)
  84  	}
  85  
  86  	// Wait for database to be ready
  87  	log.I.F("waiting for database to be ready...")
  88  	<-db.Ready()
  89  	log.I.F("database ready")
  90  
  91  	// Create and start gRPC server
  92  	serverCfg := &server.Config{
  93  		Listen:          cfg.Listen,
  94  		LogLevel:        cfg.LogLevel,
  95  		StreamBatchSize: cfg.StreamBatchSize,
  96  	}
  97  
  98  	srv := server.New(db, serverCfg)
  99  	if err := srv.ListenAndServe(ctx, cancel); err != nil {
 100  		log.E.F("gRPC server error: %v", err)
 101  	}
 102  }
 103  
 104  func loadConfig() *Config {
 105  	cfg := &Config{}
 106  	if err := env.Load(cfg, nil); chk.E(err) {
 107  		log.E.F("failed to load config: %v", err)
 108  		os.Exit(1)
 109  	}
 110  
 111  	return cfg
 112  }
 113