Browse Source

Refactor database configuration to use centralized struct

Replaced individual environment variable access with a unified `DatabaseConfig` struct for all database backends. This centralizes configuration management, reduces redundant code, and ensures all options are documented in `app/config/config.go`. Backward compatibility is maintained with default values and retained constructors.
main
mleku 1 month ago
parent
commit
016e97925a
No known key found for this signature in database
  1. 20
      CLAUDE.md
  2. 43
      app/config/config.go
  3. 4
      docs/NEO4J_BACKEND.md
  4. 33
      main.go
  5. 100
      pkg/database/database.go
  6. 67
      pkg/database/factory.go
  7. 13
      pkg/database/save-event.go
  8. 48
      pkg/dgraph/dgraph.go
  9. 4
      pkg/neo4j/README.md
  10. 52
      pkg/neo4j/neo4j.go
  11. 2
      pkg/version/version

20
CLAUDE.md

@ -139,9 +139,16 @@ export ORLY_SPROCKET_ENABLED=true
# Enable policy system # Enable policy system
export ORLY_POLICY_ENABLED=true export ORLY_POLICY_ENABLED=true
# Database backend selection (badger or dgraph) # Database backend selection (badger, dgraph, or neo4j)
export ORLY_DB_TYPE=badger export ORLY_DB_TYPE=badger
export ORLY_DGRAPH_URL=localhost:9080 # Only for dgraph backend
# DGraph configuration (only when ORLY_DB_TYPE=dgraph)
export ORLY_DGRAPH_URL=localhost:9080
# Neo4j configuration (only when ORLY_DB_TYPE=neo4j)
export ORLY_NEO4J_URI=bolt://localhost:7687
export ORLY_NEO4J_USER=neo4j
export ORLY_NEO4J_PASSWORD=password
# Query cache configuration (improves REQ response times) # Query cache configuration (improves REQ response times)
export ORLY_QUERY_CACHE_SIZE_MB=512 # Default: 512MB export ORLY_QUERY_CACHE_SIZE_MB=512 # Default: 512MB
@ -150,6 +157,7 @@ export ORLY_QUERY_CACHE_MAX_AGE=5m # Cache expiry time
# Database cache tuning (for Badger backend) # Database cache tuning (for Badger backend)
export ORLY_DB_BLOCK_CACHE_MB=512 # Block cache size export ORLY_DB_BLOCK_CACHE_MB=512 # Block cache size
export ORLY_DB_INDEX_CACHE_MB=256 # Index cache size export ORLY_DB_INDEX_CACHE_MB=256 # Index cache size
export ORLY_INLINE_EVENT_THRESHOLD=1024 # Inline storage threshold (bytes)
``` ```
## Code Architecture ## Code Architecture
@ -299,9 +307,15 @@ export ORLY_DB_INDEX_CACHE_MB=256 # Index cache size
**Configuration System:** **Configuration System:**
- Uses `go-simpler.org/env` for struct tags - Uses `go-simpler.org/env` for struct tags
- All config in `app/config/config.go` with `ORLY_` prefix - **IMPORTANT: ALL environment variables MUST be defined in `app/config/config.go`**
- Never use `os.Getenv()` directly in packages - always pass config via structs
- This ensures all config options appear in `./orly help` output
- Database backends receive config via `database.DatabaseConfig` struct
- Use `GetDatabaseConfigValues()` helper to extract DB config from app config
- All config fields use `ORLY_` prefix with struct tags defining defaults and usage
- Supports XDG directories via `github.com/adrg/xdg` - Supports XDG directories via `github.com/adrg/xdg`
- Default data directory: `~/.local/share/ORLY` - Default data directory: `~/.local/share/ORLY`
- Database-specific config (Neo4j, DGraph, Badger) is passed via `DatabaseConfig` struct in `pkg/database/factory.go`
**Event Publishing:** **Event Publishing:**
- `pkg/protocol/publish/` manages publisher registry - `pkg/protocol/publish/` manages publisher registry

43
app/config/config.go

@ -1,5 +1,13 @@
// Package config provides a go-simpler.org/env configuration table and helpers // Package config provides a go-simpler.org/env configuration table and helpers
// for working with the list of key/value lists stored in .env files. // for working with the list of key/value lists stored in .env files.
//
// IMPORTANT: This file is the SINGLE SOURCE OF TRUTH for all environment variables.
// All configuration options MUST be defined here with proper `env` struct tags.
// Never use os.Getenv() directly in other packages - pass configuration via structs.
// This ensures all options appear in `./orly help` output and are documented.
//
// For database backends, use GetDatabaseConfigValues() to extract database-specific
// settings, then construct a database.DatabaseConfig in the caller (e.g., main.go).
package config package config
import ( import (
@ -82,11 +90,19 @@ type C struct {
NIP43InviteExpiry time.Duration `env:"ORLY_NIP43_INVITE_EXPIRY" default:"24h" usage:"how long invite codes remain valid"` NIP43InviteExpiry time.Duration `env:"ORLY_NIP43_INVITE_EXPIRY" default:"24h" usage:"how long invite codes remain valid"`
// Database configuration // Database configuration
DBType string `env:"ORLY_DB_TYPE" default:"badger" usage:"database backend to use: badger or dgraph"` DBType string `env:"ORLY_DB_TYPE" default:"badger" usage:"database backend to use: badger, dgraph, or neo4j"`
DgraphURL string `env:"ORLY_DGRAPH_URL" default:"localhost:9080" usage:"dgraph gRPC endpoint address (only used when ORLY_DB_TYPE=dgraph)"` DgraphURL string `env:"ORLY_DGRAPH_URL" default:"localhost:9080" usage:"dgraph gRPC endpoint address (only used when ORLY_DB_TYPE=dgraph)"`
QueryCacheSizeMB int `env:"ORLY_QUERY_CACHE_SIZE_MB" default:"512" usage:"query cache size in MB (caches database query results for faster REQ responses)"` QueryCacheSizeMB int `env:"ORLY_QUERY_CACHE_SIZE_MB" default:"512" usage:"query cache size in MB (caches database query results for faster REQ responses)"`
QueryCacheMaxAge string `env:"ORLY_QUERY_CACHE_MAX_AGE" default:"5m" usage:"maximum age for cached query results (e.g., 5m, 10m, 1h)"` QueryCacheMaxAge string `env:"ORLY_QUERY_CACHE_MAX_AGE" default:"5m" usage:"maximum age for cached query results (e.g., 5m, 10m, 1h)"`
// Neo4j configuration (only used when ORLY_DB_TYPE=neo4j)
Neo4jURI string `env:"ORLY_NEO4J_URI" default:"bolt://localhost:7687" usage:"Neo4j bolt URI (only used when ORLY_DB_TYPE=neo4j)"`
Neo4jUser string `env:"ORLY_NEO4J_USER" default:"neo4j" usage:"Neo4j authentication username (only used when ORLY_DB_TYPE=neo4j)"`
Neo4jPassword string `env:"ORLY_NEO4J_PASSWORD" default:"password" usage:"Neo4j authentication password (only used when ORLY_DB_TYPE=neo4j)"`
// Advanced database tuning
InlineEventThreshold int `env:"ORLY_INLINE_EVENT_THRESHOLD" default:"1024" usage:"size threshold in bytes for inline event storage in Badger (0 to disable, typical values: 384-1024)"`
// TLS configuration // TLS configuration
TLSDomains []string `env:"ORLY_TLS_DOMAINS" usage:"comma-separated list of domains to respond to for TLS"` TLSDomains []string `env:"ORLY_TLS_DOMAINS" usage:"comma-separated list of domains to respond to for TLS"`
Certs []string `env:"ORLY_CERTS" usage:"comma-separated list of paths to certificate root names (e.g., /path/to/cert will load /path/to/cert.pem and /path/to/cert.key)"` Certs []string `env:"ORLY_CERTS" usage:"comma-separated list of paths to certificate root names (e.g., /path/to/cert will load /path/to/cert.pem and /path/to/cert.key)"`
@ -369,3 +385,28 @@ func PrintHelp(cfg *C, printer io.Writer) {
PrintEnv(cfg, printer) PrintEnv(cfg, printer)
fmt.Fprintln(printer) fmt.Fprintln(printer)
} }
// GetDatabaseConfigValues returns the database configuration values as individual fields.
// This avoids circular imports with pkg/database while allowing main.go to construct
// a database.DatabaseConfig with the correct type.
func (cfg *C) GetDatabaseConfigValues() (
dataDir, logLevel string,
blockCacheMB, indexCacheMB, queryCacheSizeMB int,
queryCacheMaxAge time.Duration,
inlineEventThreshold int,
dgraphURL, neo4jURI, neo4jUser, neo4jPassword string,
) {
// Parse query cache max age from string to duration
queryCacheMaxAge = 5 * time.Minute // Default
if cfg.QueryCacheMaxAge != "" {
if duration, err := time.ParseDuration(cfg.QueryCacheMaxAge); err == nil {
queryCacheMaxAge = duration
}
}
return cfg.DataDir, cfg.DBLogLevel,
cfg.DBBlockCacheMB, cfg.DBIndexCacheMB, cfg.QueryCacheSizeMB,
queryCacheMaxAge,
cfg.InlineEventThreshold,
cfg.DgraphURL, cfg.Neo4jURI, cfg.Neo4jUser, cfg.Neo4jPassword
}

4
docs/NEO4J_BACKEND.md

@ -177,6 +177,10 @@ LIMIT $limit
## Configuration ## Configuration
All configuration is centralized in `app/config/config.go` and visible via `./orly help`.
> **Important:** All environment variables must be defined in `app/config/config.go`. Do not use `os.Getenv()` directly in package code. Database backends receive configuration via the `database.DatabaseConfig` struct.
### Environment Variables ### Environment Variables
```bash ```bash

33
main.go

@ -42,8 +42,8 @@ func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
var db database.Database var db database.Database
if db, err = database.NewDatabase( if db, err = database.NewDatabaseWithConfig(
ctx, cancel, cfg.DBType, cfg.DataDir, cfg.DBLogLevel, ctx, cancel, cfg.DBType, makeDatabaseConfig(cfg),
); chk.E(err) { ); chk.E(err) {
os.Exit(1) os.Exit(1)
} }
@ -318,8 +318,8 @@ func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
var db database.Database var db database.Database
log.I.F("initializing %s database at %s", cfg.DBType, cfg.DataDir) log.I.F("initializing %s database at %s", cfg.DBType, cfg.DataDir)
if db, err = database.NewDatabase( if db, err = database.NewDatabaseWithConfig(
ctx, cancel, cfg.DBType, cfg.DataDir, cfg.DBLogLevel, ctx, cancel, cfg.DBType, makeDatabaseConfig(cfg),
); chk.E(err) { ); chk.E(err) {
os.Exit(1) os.Exit(1)
} }
@ -430,3 +430,28 @@ func main() {
} }
// log.I.F("exiting") // log.I.F("exiting")
} }
// makeDatabaseConfig creates a database.DatabaseConfig from the app config.
// This helper function extracts all database-specific configuration values
// and constructs the appropriate struct for the database package.
func makeDatabaseConfig(cfg *config.C) *database.DatabaseConfig {
dataDir, logLevel,
blockCacheMB, indexCacheMB, queryCacheSizeMB,
queryCacheMaxAge,
inlineEventThreshold,
dgraphURL, neo4jURI, neo4jUser, neo4jPassword := cfg.GetDatabaseConfigValues()
return &database.DatabaseConfig{
DataDir: dataDir,
LogLevel: logLevel,
BlockCacheMB: blockCacheMB,
IndexCacheMB: indexCacheMB,
QueryCacheSizeMB: queryCacheSizeMB,
QueryCacheMaxAge: queryCacheMaxAge,
InlineEventThreshold: inlineEventThreshold,
DgraphURL: dgraphURL,
Neo4jURI: neo4jURI,
Neo4jUser: neo4jUser,
Neo4jPassword: neo4jPassword,
}
}

100
pkg/database/database.go

@ -5,7 +5,6 @@ import (
"errors" "errors"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"time" "time"
"github.com/dgraph-io/badger/v4" "github.com/dgraph-io/badger/v4"
@ -21,10 +20,11 @@ import (
// D implements the Database interface using Badger as the storage backend // D implements the Database interface using Badger as the storage backend
type D struct { type D struct {
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
dataDir string dataDir string
Logger *logger Logger *logger
inlineEventThreshold int // Configurable threshold for inline event storage
*badger.DB *badger.DB
seq *badger.Sequence seq *badger.Sequence
pubkeySeq *badger.Sequence // Sequence for pubkey serials pubkeySeq *badger.Sequence // Sequence for pubkey serials
@ -35,63 +35,85 @@ type D struct {
// Ensure D implements Database interface at compile time // Ensure D implements Database interface at compile time
var _ Database = (*D)(nil) var _ Database = (*D)(nil)
// New creates a new Badger database instance with default configuration.
// This is provided for backward compatibility with existing callers.
// For full configuration control, use NewWithConfig instead.
func New( func New(
ctx context.Context, cancel context.CancelFunc, dataDir, logLevel string, ctx context.Context, cancel context.CancelFunc, dataDir, logLevel string,
) ( ) (
d *D, err error, d *D, err error,
) { ) {
// Initialize query cache with configurable size (default 512MB) // Create a default config for backward compatibility
queryCacheSize := int64(512 * 1024 * 1024) // 512 MB cfg := &DatabaseConfig{
if v := os.Getenv("ORLY_QUERY_CACHE_SIZE_MB"); v != "" { DataDir: dataDir,
if n, perr := strconv.Atoi(v); perr == nil && n > 0 { LogLevel: logLevel,
queryCacheSize = int64(n * 1024 * 1024) BlockCacheMB: 1024, // Default 1024 MB
} IndexCacheMB: 512, // Default 512 MB
QueryCacheSizeMB: 512, // Default 512 MB
QueryCacheMaxAge: 5 * time.Minute, // Default 5 minutes
InlineEventThreshold: 1024, // Default 1024 bytes
} }
queryCacheMaxAge := 5 * time.Minute // Default 5 minutes return NewWithConfig(ctx, cancel, cfg)
if v := os.Getenv("ORLY_QUERY_CACHE_MAX_AGE"); v != "" { }
if duration, perr := time.ParseDuration(v); perr == nil {
queryCacheMaxAge = duration // NewWithConfig creates a new Badger database instance with full configuration.
} // This is the preferred method when you have access to DatabaseConfig.
func NewWithConfig(
ctx context.Context, cancel context.CancelFunc, cfg *DatabaseConfig,
) (
d *D, err error,
) {
// Apply defaults for zero values (backward compatibility)
blockCacheMB := cfg.BlockCacheMB
if blockCacheMB == 0 {
blockCacheMB = 1024 // Default 1024 MB
}
indexCacheMB := cfg.IndexCacheMB
if indexCacheMB == 0 {
indexCacheMB = 512 // Default 512 MB
} }
queryCacheSizeMB := cfg.QueryCacheSizeMB
if queryCacheSizeMB == 0 {
queryCacheSizeMB = 512 // Default 512 MB
}
queryCacheMaxAge := cfg.QueryCacheMaxAge
if queryCacheMaxAge == 0 {
queryCacheMaxAge = 5 * time.Minute // Default 5 minutes
}
inlineEventThreshold := cfg.InlineEventThreshold
if inlineEventThreshold == 0 {
inlineEventThreshold = 1024 // Default 1024 bytes
}
queryCacheSize := int64(queryCacheSizeMB * 1024 * 1024)
d = &D{ d = &D{
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
dataDir: dataDir, dataDir: cfg.DataDir,
Logger: NewLogger(lol.GetLogLevel(logLevel), dataDir), Logger: NewLogger(lol.GetLogLevel(cfg.LogLevel), cfg.DataDir),
DB: nil, inlineEventThreshold: inlineEventThreshold,
seq: nil, DB: nil,
ready: make(chan struct{}), seq: nil,
queryCache: querycache.NewEventCache(queryCacheSize, queryCacheMaxAge), ready: make(chan struct{}),
queryCache: querycache.NewEventCache(queryCacheSize, queryCacheMaxAge),
} }
// Ensure the data directory exists // Ensure the data directory exists
if err = os.MkdirAll(dataDir, 0755); chk.E(err) { if err = os.MkdirAll(cfg.DataDir, 0755); chk.E(err) {
return return
} }
// Also ensure the directory exists using apputil.EnsureDir for any // Also ensure the directory exists using apputil.EnsureDir for any
// potential subdirectories // potential subdirectories
dummyFile := filepath.Join(dataDir, "dummy.sst") dummyFile := filepath.Join(cfg.DataDir, "dummy.sst")
if err = apputil.EnsureDir(dummyFile); chk.E(err) { if err = apputil.EnsureDir(dummyFile); chk.E(err) {
return return
} }
opts := badger.DefaultOptions(d.dataDir) opts := badger.DefaultOptions(d.dataDir)
// Configure caches based on environment to better match workload. // Configure caches based on config to better match workload.
// Defaults aim for higher hit ratios under read-heavy workloads while remaining safe. // Defaults aim for higher hit ratios under read-heavy workloads while remaining safe.
var blockCacheMB = 1024 // default 512 MB
var indexCacheMB = 512 // default 256 MB
if v := os.Getenv("ORLY_DB_BLOCK_CACHE_MB"); v != "" {
if n, perr := strconv.Atoi(v); perr == nil && n > 0 {
blockCacheMB = n
}
}
if v := os.Getenv("ORLY_DB_INDEX_CACHE_MB"); v != "" {
if n, perr := strconv.Atoi(v); perr == nil && n > 0 {
indexCacheMB = n
}
}
opts.BlockCacheSize = int64(blockCacheMB * units.Mb) opts.BlockCacheSize = int64(blockCacheMB * units.Mb)
opts.IndexCacheSize = int64(indexCacheMB * units.Mb) opts.IndexCacheSize = int64(indexCacheMB * units.Mb)
opts.BlockSize = 4 * units.Kb // 4 KB block size opts.BlockSize = 4 * units.Kb // 4 KB block size

67
pkg/database/factory.go

@ -4,8 +4,33 @@ import (
"context" "context"
"fmt" "fmt"
"strings" "strings"
"time"
) )
// DatabaseConfig holds all database configuration options that can be passed
// to any database backend. Each backend uses the relevant fields for its type.
// This centralizes configuration instead of having each backend read env vars directly.
type DatabaseConfig struct {
// Common settings for all backends
DataDir string
LogLevel string
// Badger-specific settings
BlockCacheMB int // ORLY_DB_BLOCK_CACHE_MB
IndexCacheMB int // ORLY_DB_INDEX_CACHE_MB
QueryCacheSizeMB int // ORLY_QUERY_CACHE_SIZE_MB
QueryCacheMaxAge time.Duration // ORLY_QUERY_CACHE_MAX_AGE
InlineEventThreshold int // ORLY_INLINE_EVENT_THRESHOLD
// DGraph-specific settings
DgraphURL string // ORLY_DGRAPH_URL
// Neo4j-specific settings
Neo4jURI string // ORLY_NEO4J_URI
Neo4jUser string // ORLY_NEO4J_USER
Neo4jPassword string // ORLY_NEO4J_PASSWORD
}
// NewDatabase creates a database instance based on the specified type. // NewDatabase creates a database instance based on the specified type.
// Supported types: "badger", "dgraph", "neo4j" // Supported types: "badger", "dgraph", "neo4j"
func NewDatabase( func NewDatabase(
@ -14,19 +39,39 @@ func NewDatabase(
dbType string, dbType string,
dataDir string, dataDir string,
logLevel string, logLevel string,
) (Database, error) {
// Create a default config for backward compatibility with existing callers
cfg := &DatabaseConfig{
DataDir: dataDir,
LogLevel: logLevel,
}
return NewDatabaseWithConfig(ctx, cancel, dbType, cfg)
}
// NewDatabaseWithConfig creates a database instance with full configuration.
// This is the preferred method when you have access to the app config.
func NewDatabaseWithConfig(
ctx context.Context,
cancel context.CancelFunc,
dbType string,
cfg *DatabaseConfig,
) (Database, error) { ) (Database, error) {
switch strings.ToLower(dbType) { switch strings.ToLower(dbType) {
case "badger", "": case "badger", "":
// Use the existing badger implementation // Use the existing badger implementation
return New(ctx, cancel, dataDir, logLevel) return NewWithConfig(ctx, cancel, cfg)
case "dgraph": case "dgraph":
// Use the new dgraph implementation // Use the dgraph implementation
// Import dynamically to avoid import cycles if newDgraphDatabase == nil {
return newDgraphDatabase(ctx, cancel, dataDir, logLevel) return nil, fmt.Errorf("dgraph database backend not available (import _ \"next.orly.dev/pkg/dgraph\")")
}
return newDgraphDatabase(ctx, cancel, cfg)
case "neo4j": case "neo4j":
// Use the new neo4j implementation // Use the neo4j implementation
// Import dynamically to avoid import cycles if newNeo4jDatabase == nil {
return newNeo4jDatabase(ctx, cancel, dataDir, logLevel) return nil, fmt.Errorf("neo4j database backend not available (import _ \"next.orly.dev/pkg/neo4j\")")
}
return newNeo4jDatabase(ctx, cancel, cfg)
default: default:
return nil, fmt.Errorf("unsupported database type: %s (supported: badger, dgraph, neo4j)", dbType) return nil, fmt.Errorf("unsupported database type: %s (supported: badger, dgraph, neo4j)", dbType)
} }
@ -34,20 +79,20 @@ func NewDatabase(
// newDgraphDatabase creates a dgraph database instance // newDgraphDatabase creates a dgraph database instance
// This is defined here to avoid import cycles // This is defined here to avoid import cycles
var newDgraphDatabase func(context.Context, context.CancelFunc, string, string) (Database, error) var newDgraphDatabase func(context.Context, context.CancelFunc, *DatabaseConfig) (Database, error)
// RegisterDgraphFactory registers the dgraph database factory // RegisterDgraphFactory registers the dgraph database factory
// This is called from the dgraph package's init() function // This is called from the dgraph package's init() function
func RegisterDgraphFactory(factory func(context.Context, context.CancelFunc, string, string) (Database, error)) { func RegisterDgraphFactory(factory func(context.Context, context.CancelFunc, *DatabaseConfig) (Database, error)) {
newDgraphDatabase = factory newDgraphDatabase = factory
} }
// newNeo4jDatabase creates a neo4j database instance // newNeo4jDatabase creates a neo4j database instance
// This is defined here to avoid import cycles // This is defined here to avoid import cycles
var newNeo4jDatabase func(context.Context, context.CancelFunc, string, string) (Database, error) var newNeo4jDatabase func(context.Context, context.CancelFunc, *DatabaseConfig) (Database, error)
// RegisterNeo4jFactory registers the neo4j database factory // RegisterNeo4jFactory registers the neo4j database factory
// This is called from the neo4j package's init() function // This is called from the neo4j package's init() function
func RegisterNeo4jFactory(factory func(context.Context, context.CancelFunc, string, string) (Database, error)) { func RegisterNeo4jFactory(factory func(context.Context, context.CancelFunc, *DatabaseConfig) (Database, error)) {
newNeo4jDatabase = factory newNeo4jDatabase = factory
} }

13
pkg/database/save-event.go

@ -5,8 +5,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"os"
"strconv"
"strings" "strings"
"github.com/dgraph-io/badger/v4" "github.com/dgraph-io/badger/v4"
@ -270,14 +268,9 @@ func (d *D) SaveEvent(c context.Context, ev *event.E) (
eventData := eventDataBuf.Bytes() eventData := eventDataBuf.Bytes()
// Determine storage strategy (Reiser4 optimizations) // Determine storage strategy (Reiser4 optimizations)
// Get threshold from environment, default to 0 (disabled) // Use the threshold from database configuration
// When enabled, typical values: 384 (conservative), 512 (recommended), 1024 (aggressive) // Typical values: 384 (conservative), 512 (recommended), 1024 (aggressive)
smallEventThreshold := 1024 smallEventThreshold := d.inlineEventThreshold
if v := os.Getenv("ORLY_INLINE_EVENT_THRESHOLD"); v != "" {
if n, perr := strconv.Atoi(v); perr == nil && n >= 0 {
smallEventThreshold = n
}
}
isSmallEvent := smallEventThreshold > 0 && len(eventData) <= smallEventThreshold isSmallEvent := smallEventThreshold > 0 && len(eventData) <= smallEventThreshold
isReplaceableEvent := kind.IsReplaceable(ev.Kind) isReplaceableEvent := kind.IsReplaceable(ev.Kind)
isAddressableEvent := kind.IsParameterizedReplaceable(ev.Kind) isAddressableEvent := kind.IsParameterizedReplaceable(ev.Kind)

48
pkg/dgraph/dgraph.go

@ -48,30 +48,21 @@ func init() {
database.RegisterDgraphFactory(func( database.RegisterDgraphFactory(func(
ctx context.Context, ctx context.Context,
cancel context.CancelFunc, cancel context.CancelFunc,
dataDir string, cfg *database.DatabaseConfig,
logLevel string,
) (database.Database, error) { ) (database.Database, error) {
return New(ctx, cancel, dataDir, logLevel) return NewWithConfig(ctx, cancel, cfg)
}) })
} }
// Config holds configuration options for the Dgraph database // NewWithConfig creates a new Dgraph-based database instance with full configuration.
type Config struct { // Configuration is passed from the centralized app config via DatabaseConfig.
DataDir string func NewWithConfig(
LogLevel string ctx context.Context, cancel context.CancelFunc, cfg *database.DatabaseConfig,
DgraphURL string // Dgraph gRPC endpoint (e.g., "localhost:9080")
EnableGraphQL bool
EnableIntrospection bool
}
// New creates a new Dgraph-based database instance
func New(
ctx context.Context, cancel context.CancelFunc, dataDir, logLevel string,
) ( ) (
d *D, err error, d *D, err error,
) { ) {
// Get dgraph URL from environment, default to localhost // Apply defaults for empty values
dgraphURL := os.Getenv("ORLY_DGRAPH_URL") dgraphURL := cfg.DgraphURL
if dgraphURL == "" { if dgraphURL == "" {
dgraphURL = "localhost:9080" dgraphURL = "localhost:9080"
} }
@ -79,8 +70,8 @@ func New(
d = &D{ d = &D{
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
dataDir: dataDir, dataDir: cfg.DataDir,
Logger: NewLogger(lol.GetLogLevel(logLevel), dataDir), Logger: NewLogger(lol.GetLogLevel(cfg.LogLevel), cfg.DataDir),
dgraphURL: dgraphURL, dgraphURL: dgraphURL,
enableGraphQL: false, enableGraphQL: false,
enableIntrospection: false, enableIntrospection: false,
@ -88,12 +79,12 @@ func New(
} }
// Ensure the data directory exists // Ensure the data directory exists
if err = os.MkdirAll(dataDir, 0755); chk.E(err) { if err = os.MkdirAll(cfg.DataDir, 0755); chk.E(err) {
return return
} }
// Ensure directory structure // Ensure directory structure
dummyFile := filepath.Join(dataDir, "dummy.sst") dummyFile := filepath.Join(cfg.DataDir, "dummy.sst")
if err = apputil.EnsureDir(dummyFile); chk.E(err) { if err = apputil.EnsureDir(dummyFile); chk.E(err) {
return return
} }
@ -128,6 +119,21 @@ func New(
return return
} }
// New creates a new Dgraph-based database instance with default configuration.
// This is provided for backward compatibility with existing callers (tests, etc.).
// For full configuration control, use NewWithConfig instead.
func New(
ctx context.Context, cancel context.CancelFunc, dataDir, logLevel string,
) (
d *D, err error,
) {
cfg := &database.DatabaseConfig{
DataDir: dataDir,
LogLevel: logLevel,
}
return NewWithConfig(ctx, cancel, cfg)
}
// initDgraphClient establishes connection to dgraph server // initDgraphClient establishes connection to dgraph server
func (d *D) initDgraphClient() error { func (d *D) initDgraphClient() error {
d.Logger.Infof("connecting to dgraph at %s", d.dgraphURL) d.Logger.Infof("connecting to dgraph at %s", d.dgraphURL)

4
pkg/neo4j/README.md

@ -15,6 +15,8 @@ docker run -d --name neo4j \
### 2. Configure Environment ### 2. Configure Environment
All Neo4j configuration is defined in `app/config/config.go` and visible via `./orly help`:
```bash ```bash
export ORLY_DB_TYPE=neo4j export ORLY_DB_TYPE=neo4j
export ORLY_NEO4J_URI=bolt://localhost:7687 export ORLY_NEO4J_URI=bolt://localhost:7687
@ -22,6 +24,8 @@ export ORLY_NEO4J_USER=neo4j
export ORLY_NEO4J_PASSWORD=password export ORLY_NEO4J_PASSWORD=password
``` ```
> **Note:** Configuration is centralized in `app/config/config.go`. Do not use `os.Getenv()` directly in package code - all environment variables should be passed via the `database.DatabaseConfig` struct.
### 3. Run ORLY ### 3. Run ORLY
```bash ```bash

52
pkg/neo4j/neo4j.go

@ -70,38 +70,29 @@ func init() {
database.RegisterNeo4jFactory(func( database.RegisterNeo4jFactory(func(
ctx context.Context, ctx context.Context,
cancel context.CancelFunc, cancel context.CancelFunc,
dataDir string, cfg *database.DatabaseConfig,
logLevel string,
) (database.Database, error) { ) (database.Database, error) {
return New(ctx, cancel, dataDir, logLevel) return NewWithConfig(ctx, cancel, cfg)
}) })
} }
// Config holds configuration options for the Neo4j database // NewWithConfig creates a new Neo4j-based database instance with full configuration.
type Config struct { // Configuration is passed from the centralized app config via DatabaseConfig.
DataDir string func NewWithConfig(
LogLevel string ctx context.Context, cancel context.CancelFunc, cfg *database.DatabaseConfig,
Neo4jURI string // Neo4j bolt URI (e.g., "bolt://localhost:7687")
Neo4jUser string // Authentication username
Neo4jPassword string // Authentication password
}
// New creates a new Neo4j-based database instance
func New(
ctx context.Context, cancel context.CancelFunc, dataDir, logLevel string,
) ( ) (
n *N, err error, n *N, err error,
) { ) {
// Get Neo4j connection details from environment // Apply defaults for empty values
neo4jURI := os.Getenv("ORLY_NEO4J_URI") neo4jURI := cfg.Neo4jURI
if neo4jURI == "" { if neo4jURI == "" {
neo4jURI = "bolt://localhost:7687" neo4jURI = "bolt://localhost:7687"
} }
neo4jUser := os.Getenv("ORLY_NEO4J_USER") neo4jUser := cfg.Neo4jUser
if neo4jUser == "" { if neo4jUser == "" {
neo4jUser = "neo4j" neo4jUser = "neo4j"
} }
neo4jPassword := os.Getenv("ORLY_NEO4J_PASSWORD") neo4jPassword := cfg.Neo4jPassword
if neo4jPassword == "" { if neo4jPassword == "" {
neo4jPassword = "password" neo4jPassword = "password"
} }
@ -109,8 +100,8 @@ func New(
n = &N{ n = &N{
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
dataDir: dataDir, dataDir: cfg.DataDir,
Logger: NewLogger(lol.GetLogLevel(logLevel), dataDir), Logger: NewLogger(lol.GetLogLevel(cfg.LogLevel), cfg.DataDir),
neo4jURI: neo4jURI, neo4jURI: neo4jURI,
neo4jUser: neo4jUser, neo4jUser: neo4jUser,
neo4jPassword: neo4jPassword, neo4jPassword: neo4jPassword,
@ -118,12 +109,12 @@ func New(
} }
// Ensure the data directory exists // Ensure the data directory exists
if err = os.MkdirAll(dataDir, 0755); chk.E(err) { if err = os.MkdirAll(cfg.DataDir, 0755); chk.E(err) {
return return
} }
// Ensure directory structure // Ensure directory structure
dummyFile := filepath.Join(dataDir, "dummy.sst") dummyFile := filepath.Join(cfg.DataDir, "dummy.sst")
if err = apputil.EnsureDir(dummyFile); chk.E(err) { if err = apputil.EnsureDir(dummyFile); chk.E(err) {
return return
} }
@ -158,6 +149,21 @@ func New(
return return
} }
// New creates a new Neo4j-based database instance with default configuration.
// This is provided for backward compatibility with existing callers (tests, etc.).
// For full configuration control, use NewWithConfig instead.
func New(
ctx context.Context, cancel context.CancelFunc, dataDir, logLevel string,
) (
n *N, err error,
) {
cfg := &database.DatabaseConfig{
DataDir: dataDir,
LogLevel: logLevel,
}
return NewWithConfig(ctx, cancel, cfg)
}
// initNeo4jClient establishes connection to Neo4j server // initNeo4jClient establishes connection to Neo4j server
func (n *N) initNeo4jClient() error { func (n *N) initNeo4jClient() error {
n.Logger.Infof("connecting to neo4j at %s", n.neo4jURI) n.Logger.Infof("connecting to neo4j at %s", n.neo4jURI)

2
pkg/version/version

@ -1 +1 @@
v0.31.7 v0.31.8
Loading…
Cancel
Save