Bugfixes, add INFO command, graceful shutdown. elimitate dead code
- Remove unsed handleClientKill, handleClientKill - Fix misleading error message on rate limit - Eliminated unused channel-to-pattern index that was causing unnecessary work - Fixed O(n²) duplicate check in PublishToPattern - Limit total pattern subscriptions to 10,000 - Limit channel subscriptions per client to 10,000 - Added INFO command - Added graceful shutdown timeout - Validate that log directory is writable during config load - Make SELECT properly handle whitespace around DB index like Redis - Return proper error instead of empty array for unsupported commands on sharded pubsub stubs - More accurate error messages throughout - Updated README.md
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -15,27 +16,29 @@ import (
|
||||
|
||||
// Constants for default values - chosen to be production-ready defaults
|
||||
const (
|
||||
DefaultBind = "0.0.0.0"
|
||||
DefaultPort = 6379
|
||||
DefaultMaxClients = 10000
|
||||
DefaultReadTimeout = 0 // 0 means no timeout (blocking I/O)
|
||||
DefaultWriteTimeout = 0 // 0 means no timeout
|
||||
DefaultMaxMsgBytes = 512 * 1024 * 1024 // 512 MB - allows large messages
|
||||
DefaultLogLevel = "info"
|
||||
DefaultLogFile = "" // empty = stdout only
|
||||
DefaultPidFile = "" // empty = no PID file created
|
||||
DefaultBind = "0.0.0.0"
|
||||
DefaultPort = 6379
|
||||
DefaultMaxClients = 10000
|
||||
DefaultReadTimeout = 0 // 0 means no timeout (blocking I/O)
|
||||
DefaultWriteTimeout = 0 // 0 means no timeout
|
||||
DefaultMaxMsgBytes = 512 * 1024 * 1024 // 512 MB - allows large messages
|
||||
DefaultLogLevel = "info"
|
||||
DefaultLogFile = "" // empty = stdout only
|
||||
DefaultPidFile = "" // empty = no PID file created
|
||||
DefaultShutdownTimeout = 30 // 30 seconds grace period for shutdown
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Bind string `json:"bind"`
|
||||
Port int `json:"port"`
|
||||
MaxClients int `json:"max_clients"`
|
||||
ReadTimeout int `json:"read_timeout"` // in seconds, 0 = no timeout
|
||||
WriteTimeout int `json:"write_timeout"` // in seconds, 0 = no timeout
|
||||
MaxMsgBytes int `json:"max_message_bytes"`
|
||||
LogLevel string `json:"log_level"`
|
||||
LogFile string `json:"log_file"` // empty = stdout only
|
||||
PidFile string `json:"pid_file"` // empty = no PID file
|
||||
Bind string `json:"bind"`
|
||||
Port int `json:"port"`
|
||||
MaxClients int `json:"max_clients"`
|
||||
ReadTimeout int `json:"read_timeout"` // in seconds, 0 = no timeout
|
||||
WriteTimeout int `json:"write_timeout"` // in seconds, 0 = no timeout
|
||||
MaxMsgBytes int `json:"max_message_bytes"`
|
||||
LogLevel string `json:"log_level"`
|
||||
LogFile string `json:"log_file"` // empty = stdout only
|
||||
PidFile string `json:"pid_file"` // empty = no PID file
|
||||
ShutdownTimeout int `json:"shutdown_timeout"` // in seconds, 0 = wait forever (not recommended)
|
||||
}
|
||||
|
||||
// SetDefaults applies sensible defaults when config values are missing or invalid.
|
||||
@@ -69,6 +72,9 @@ func (c *Config) SetDefaults() {
|
||||
if c.PidFile == "" {
|
||||
c.PidFile = DefaultPidFile
|
||||
}
|
||||
if c.ShutdownTimeout <= 0 {
|
||||
c.ShutdownTimeout = DefaultShutdownTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Validate checks that all configuration values are within acceptable bounds.
|
||||
@@ -99,6 +105,37 @@ func (c *Config) Validate() error {
|
||||
if !validLevels[c.LogLevel] {
|
||||
return fmt.Errorf("invalid log_level: %s (must be debug, info, warn, or error)", c.LogLevel)
|
||||
}
|
||||
// Validate log file directory is writable
|
||||
if c.LogFile != "" {
|
||||
dir := filepath.Dir(c.LogFile)
|
||||
if err := testWriteDir(dir); err != nil {
|
||||
return fmt.Errorf("log_file: %w", err)
|
||||
}
|
||||
}
|
||||
// Validate shutdown timeout is non-negative
|
||||
if c.ShutdownTimeout < 0 {
|
||||
return fmt.Errorf("shutdown_timeout must be non-negative")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// testWriteDir checks if a directory is writable by trying to create a temp file
|
||||
func testWriteDir(dir string) error {
|
||||
// If directory doesn't exist, check parent
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
parent := filepath.Dir(dir)
|
||||
if parent == dir {
|
||||
return fmt.Errorf("directory does not exist: %s", dir)
|
||||
}
|
||||
return testWriteDir(parent)
|
||||
}
|
||||
// Directory exists - try to create a temp file
|
||||
tmp, err := os.CreateTemp(dir, ".pekoe_test_")
|
||||
if err != nil {
|
||||
return fmt.Errorf("directory not writable: %s", dir)
|
||||
}
|
||||
_ = tmp.Close()
|
||||
_ = os.Remove(tmp.Name())
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user