Compare commits

...

9 Commits

Author SHA1 Message Date
Juan Calderon-Perez
9951538c9e Normalize Rueidis README indentation 2025-10-13 21:33:50 -04:00
Juan Calderon-Perez
50d99b709c Align Rueidis README indentation 2025-10-13 21:33:34 -04:00
Juan Calderon-Perez
02a5e617ae Fix Rueidis README indentation 2025-10-13 21:33:16 -04:00
Juan Calderon-Perez
2fdcd6aba5 Handle rueidis startup check failures 2025-10-13 21:33:01 -04:00
Juan Calderon-Perez
c285284184 Fix rueidis startup check handling and revert mysql changes 2025-10-13 18:33:06 -04:00
Juan Calderon-Perez
75a86506f7 Revert rueidis lazy initialization 2025-10-13 17:51:07 -04:00
Juan Calderon-Perez
1782bd5841 Avoid rueidis panic when startup checks disabled 2025-10-13 17:51:01 -04:00
Juan Calderon-Perez
5652ca01b7 Add DisableStartupCheck support to Rueidis driver 2025-10-13 13:37:29 -04:00
Juan Calderon-Perez
92da8e1d90 Revert DisableStartupCheck changes for select drivers 2025-10-13 13:13:36 -04:00
16 changed files with 271 additions and 139 deletions

View File

@@ -73,6 +73,11 @@ type Config struct {
// //
// Optional. Default is false // Optional. Default is false
Reset bool Reset bool
// DisableStartupCheck skips the initial connection validation during New.
//
// Optional. Default is false
DisableStartupCheck bool
} }
``` ```
@@ -80,5 +85,6 @@ type Config struct {
```go ```go
var ConfigDefault = Config{ var ConfigDefault = Config{
Servers: "127.0.0.1:11211", Servers: "127.0.0.1:11211",
DisableStartupCheck: false,
} }
``` ```

View File

@@ -15,6 +15,11 @@ type Config struct {
// Optional. Default is false // Optional. Default is false
Reset bool Reset bool
// DisableStartupCheck skips the initial connection validation during New.
//
// Optional. Default is false
DisableStartupCheck bool
// The socket read/write timeout. // The socket read/write timeout.
// //
// Optional. Default is 100 * time.Millisecond // Optional. Default is 100 * time.Millisecond
@@ -31,9 +36,10 @@ type Config struct {
// ConfigDefault is the default config // ConfigDefault is the default config
var ConfigDefault = Config{ var ConfigDefault = Config{
Servers: "127.0.0.1:11211", Servers: "127.0.0.1:11211",
timeout: 100 * time.Millisecond, timeout: 100 * time.Millisecond,
maxIdleConns: 2, maxIdleConns: 2,
DisableStartupCheck: false,
} }
// Helper function to set default values // Helper function to set default values

View File

@@ -30,15 +30,17 @@ func New(config ...Config) *Storage {
db.Timeout = cfg.timeout db.Timeout = cfg.timeout
db.MaxIdleConns = cfg.maxIdleConns db.MaxIdleConns = cfg.maxIdleConns
// Ping database to ensure a connection has been made if !cfg.DisableStartupCheck {
if err := db.Ping(); err != nil { // Ping database to ensure a connection has been made
panic(err) if err := db.Ping(); err != nil {
}
if cfg.Reset {
if err := db.DeleteAll(); err != nil {
panic(err) panic(err)
} }
if cfg.Reset {
if err := db.DeleteAll(); err != nil {
panic(err)
}
}
} }
// Create storage // Create storage

View File

@@ -178,6 +178,16 @@ func Test_Memcache_Reset(t *testing.T) {
require.Zero(t, len(result)) require.Zero(t, len(result))
} }
func Test_Memcache_New_DisableStartupCheck(t *testing.T) {
require.NotPanics(t, func() {
store := New(Config{
Servers: "127.0.0.1:11210",
DisableStartupCheck: true,
})
require.NotNil(t, store)
})
}
func Test_Memcache_Close(t *testing.T) { func Test_Memcache_Close(t *testing.T) {
testStore := newTestStore(t) testStore := newTestStore(t)
require.Nil(t, testStore.Close()) require.Nil(t, testStore.Close())

View File

@@ -56,10 +56,10 @@ store := postgres.New()
// Initialize custom config // Initialize custom config
store := postgres.New(postgres.Config{ store := postgres.New(postgres.Config{
DB: dbPool, DB: dbPool,
Table: "fiber_storage", Table: "fiber_storage",
Reset: false, Reset: false,
GCInterval: 10 * time.Second, GCInterval: 10 * time.Second,
}) })
``` ```
@@ -67,60 +67,65 @@ store := postgres.New(postgres.Config{
```go ```go
// Config defines the config for storage. // Config defines the config for storage.
type Config struct { type Config struct {
// DB pgxpool.Pool object will override connection uri and other connection fields // DB pgxpool.Pool object will override connection uri and other connection fields
// //
// Optional. Default is nil // Optional. Default is nil
DB *pgxpool.Pool DB *pgxpool.Pool
// Connection string to use for DB. Will override all other authentication values if used // Connection string to use for DB. Will override all other authentication values if used
// //
// Optional. Default is "" // Optional. Default is ""
ConnectionURI string ConnectionURI string
// Host name where the DB is hosted // Host name where the DB is hosted
// //
// Optional. Default is "127.0.0.1" // Optional. Default is "127.0.0.1"
Host string Host string
// Port where the DB is listening on // Port where the DB is listening on
// //
// Optional. Default is 5432 // Optional. Default is 5432
Port int Port int
// Server username // Server username
// //
// Optional. Default is "" // Optional. Default is ""
Username string Username string
// Server password // Server password
// //
// Optional. Default is "" // Optional. Default is ""
Password string Password string
// Database name // Database name
// //
// Optional. Default is "fiber" // Optional. Default is "fiber"
Database string Database string
// Table name // Table name
// //
// Optional. Default is "fiber_storage" // Optional. Default is "fiber_storage"
Table string Table string
// The SSL mode for the connection // The SSL mode for the connection
// //
// Optional. Default is "disable" // Optional. Default is "disable"
SSLMode string SSLMode string
// Reset clears any existing keys in existing Table // Reset clears any existing keys in existing Table
// //
// Optional. Default is false // Optional. Default is false
Reset bool Reset bool
// Time before deleting expired keys // DisableStartupCheck skips the initial connection validation during New.
// //
// Optional. Default is 10 * time.Second // Optional. Default is false
GCInterval time.Duration DisableStartupCheck bool
// Time before deleting expired keys
//
// Optional. Default is 10 * time.Second
GCInterval time.Duration
} }
``` ```
@@ -128,13 +133,14 @@ type Config struct {
```go ```go
// ConfigDefault is the default config // ConfigDefault is the default config
var ConfigDefault = Config{ var ConfigDefault = Config{
ConnectionURI: "", ConnectionURI: "",
Host: "127.0.0.1", Host: "127.0.0.1",
Port: 5432, Port: 5432,
Database: "fiber", Database: "fiber",
Table: "fiber_storage", Table: "fiber_storage",
SSLMode: "disable", SSLMode: "disable",
Reset: false, Reset: false,
GCInterval: 10 * time.Second, DisableStartupCheck: false,
GCInterval: 10 * time.Second,
} }
``` ```

View File

@@ -61,6 +61,11 @@ type Config struct {
// Optional. Default is false // Optional. Default is false
Reset bool Reset bool
// DisableStartupCheck skips the initial connection validation during New.
//
// Optional. Default is false
DisableStartupCheck bool
// Time before deleting expired keys // Time before deleting expired keys
// //
// Optional. Default is 10 * time.Second // Optional. Default is 10 * time.Second
@@ -69,14 +74,15 @@ type Config struct {
// ConfigDefault is the default config // ConfigDefault is the default config
var ConfigDefault = Config{ var ConfigDefault = Config{
ConnectionURI: "", ConnectionURI: "",
Host: "127.0.0.1", Host: "127.0.0.1",
Port: 5432, Port: 5432,
Database: "fiber", Database: "fiber",
Table: "fiber_storage", Table: "fiber_storage",
SSLMode: "disable", SSLMode: "disable",
Reset: false, Reset: false,
GCInterval: 10 * time.Second, DisableStartupCheck: false,
GCInterval: 10 * time.Second,
} }
func (c *Config) getDSN() string { func (c *Config) getDSN() string {

View File

@@ -68,43 +68,45 @@ func New(config ...Config) *Storage {
} }
} }
// Ping database if !cfg.DisableStartupCheck {
if err := db.Ping(context.Background()); err != nil { // Ping database
panic(err) if err := db.Ping(context.Background()); err != nil {
} panic(err)
}
// Parse out schema in config, if provided // Parse out schema in config, if provided
schema := "public" schema := "public"
tableName := cfg.Table tableName := cfg.Table
if strings.Contains(cfg.Table, ".") { if strings.Contains(cfg.Table, ".") {
schema = strings.Split(cfg.Table, ".")[0] schema = strings.Split(cfg.Table, ".")[0]
tableName = strings.Split(cfg.Table, ".")[1] tableName = strings.Split(cfg.Table, ".")[1]
} }
// Drop table if set to true // Drop table if set to true
if cfg.Reset { if cfg.Reset {
if _, err := db.Exec(context.Background(), fmt.Sprintf(dropQuery, cfg.Table)); err != nil { if _, err := db.Exec(context.Background(), fmt.Sprintf(dropQuery, cfg.Table)); err != nil {
db.Close()
panic(err)
}
}
// Determine if table exists
tableExists := false
row := db.QueryRow(context.Background(), fmt.Sprintf(checkTableExistsQuery, schema, tableName))
var count int
if err := row.Scan(&count); err != nil {
db.Close() db.Close()
panic(err) panic(err)
} }
} tableExists = count > 0
// Determine if table exists // Init database queries
tableExists := false if !tableExists {
row := db.QueryRow(context.Background(), fmt.Sprintf(checkTableExistsQuery, schema, tableName)) for _, query := range initQuery {
var count int if _, err := db.Exec(context.Background(), fmt.Sprintf(query, cfg.Table)); err != nil {
if err := row.Scan(&count); err != nil { db.Close()
db.Close() panic(err)
panic(err) }
}
tableExists = count > 0
// Init database queries
if !tableExists {
for _, query := range initQuery {
if _, err := db.Exec(context.Background(), fmt.Sprintf(query, cfg.Table)); err != nil {
db.Close()
panic(err)
} }
} }
} }
@@ -121,7 +123,9 @@ func New(config ...Config) *Storage {
sqlGC: fmt.Sprintf("DELETE FROM %s WHERE e <= $1 AND e != 0", cfg.Table), sqlGC: fmt.Sprintf("DELETE FROM %s WHERE e <= $1 AND e != 0", cfg.Table),
} }
store.checkSchema(cfg.Table) if !cfg.DisableStartupCheck {
store.checkSchema(cfg.Table)
}
// Start garbage collector // Start garbage collector
go store.gcTicker() go store.gcTicker()

View File

@@ -202,6 +202,18 @@ func TestNoCreateUser(t *testing.T) {
}) })
} }
func Test_Postgres_New_DisableStartupCheck(t *testing.T) {
require.NotPanics(t, func() {
store := New(Config{
Host: "127.0.0.1",
Port: 65432,
DisableStartupCheck: true,
})
require.NotNil(t, store)
defer store.Close()
})
}
func Test_Should_Panic_On_Wrong_Schema(t *testing.T) { func Test_Should_Panic_On_Wrong_Schema(t *testing.T) {
testStore := newTestStore(t) testStore := newTestStore(t)
defer testStore.Close() defer testStore.Close()

View File

@@ -180,6 +180,11 @@ type Config struct {
// Optional. Default is false // Optional. Default is false
Reset bool Reset bool
// DisableStartupCheck skips the initial connection validation during New.
//
// Optional. Default is false
DisableStartupCheck bool
// TLS Config to use. When set TLS will be negotiated. // TLS Config to use. When set TLS will be negotiated.
// //
// Optional. Default is nil // Optional. Default is nil
@@ -208,6 +213,7 @@ var ConfigDefault = Config{
URL: "", URL: "",
Database: 0, Database: 0,
Reset: false, Reset: false,
DisableStartupCheck: false,
TLSConfig: nil, TLSConfig: nil,
PoolSize: 10 * runtime.GOMAXPROCS(0), PoolSize: 10 * runtime.GOMAXPROCS(0),
Addrs: []string{}, Addrs: []string{},

View File

@@ -68,6 +68,11 @@ type Config struct {
// Optional. Default is false // Optional. Default is false
Reset bool Reset bool
// DisableStartupCheck skips the initial connection validation during New.
//
// Optional. Default is false
DisableStartupCheck bool
// TLS Config to use. When set TLS will be negotiated. // TLS Config to use. When set TLS will be negotiated.
// //
// Optional. Default is nil // Optional. Default is nil
@@ -87,21 +92,22 @@ type Config struct {
// ConfigDefault is the default config // ConfigDefault is the default config
var ConfigDefault = Config{ var ConfigDefault = Config{
Host: "127.0.0.1", Host: "127.0.0.1",
Port: 6379, Port: 6379,
Username: "", Username: "",
Password: "", Password: "",
URL: "", URL: "",
Database: 0, Database: 0,
Reset: false, Reset: false,
TLSConfig: nil, DisableStartupCheck: false,
PoolSize: 10 * runtime.GOMAXPROCS(0), TLSConfig: nil,
Addrs: []string{}, PoolSize: 10 * runtime.GOMAXPROCS(0),
MasterName: "", Addrs: []string{},
ClientName: "", MasterName: "",
SentinelUsername: "", ClientName: "",
SentinelPassword: "", SentinelUsername: "",
IsClusterMode: false, SentinelPassword: "",
IsClusterMode: false,
} }
// Helper function to set default values // Helper function to set default values

View File

@@ -65,16 +65,18 @@ func New(config ...Config) *Storage {
IsClusterMode: cfg.IsClusterMode, IsClusterMode: cfg.IsClusterMode,
}) })
// Test connection if !cfg.DisableStartupCheck {
if err := db.Ping(context.Background()).Err(); err != nil { // Test connection
panic(err) if err := db.Ping(context.Background()).Err(); err != nil {
}
// Empty collection if Clear is true
if cfg.Reset {
if err := db.FlushDB(context.Background()).Err(); err != nil {
panic(err) panic(err)
} }
// Empty collection if Clear is true
if cfg.Reset {
if err := db.FlushDB(context.Background()).Err(); err != nil {
panic(err)
}
}
} }
// Create new store // Create new store

View File

@@ -198,6 +198,19 @@ func Test_Redis_Delete(t *testing.T) {
require.Nil(t, keys) require.Nil(t, keys)
} }
func Test_Redis_New_DisableStartupCheck(t *testing.T) {
require.NotPanics(t, func() {
store := New(Config{
Host: "127.0.0.1",
Port: 6390,
Addrs: []string{"127.0.0.1:6390"},
DisableStartupCheck: true,
})
require.NotNil(t, store)
_ = store.Close()
})
}
func Test_Redis_DeleteWithContext(t *testing.T) { func Test_Redis_DeleteWithContext(t *testing.T) {
var ( var (
key = "john" key = "john"

View File

@@ -184,6 +184,13 @@ type Config struct {
// Optional. Default is false // Optional. Default is false
Reset bool Reset bool
// DisableStartupCheck skips the initial connection validation during New.
// When true and client creation fails, New returns a Storage whose
// operations surface the initialization error instead of panicking.
//
// Optional. Default is false
DisableStartupCheck bool
// CacheTTL TTL // CacheTTL TTL
// //
// Optional. Default is time.Minute // Optional. Default is time.Minute
@@ -210,6 +217,7 @@ var ConfigDefault = Config{
DisableCache: false, DisableCache: false,
AlwaysPipelining: true, AlwaysPipelining: true,
Reset: false, Reset: false,
DisableStartupCheck: false,
CacheTTL: time.Minute, CacheTTL: time.Minute,
} }
``` ```

View File

@@ -95,6 +95,13 @@ type Config struct {
// Optional. Default is false // Optional. Default is false
Reset bool Reset bool
// DisableStartupCheck skips the initial connection validation during New.
// When true and the client cannot be created, New returns a Storage whose
// operations will report the initialization error instead of panicking.
//
// Optional. Default is false
DisableStartupCheck bool
// CacheTTL TTL // CacheTTL TTL
// //
// Optional. Default is time.Minute // Optional. Default is time.Minute
@@ -120,6 +127,7 @@ var ConfigDefault = Config{
DisableCache: false, DisableCache: false,
AlwaysPipelining: true, AlwaysPipelining: true,
Reset: false, Reset: false,
DisableStartupCheck: false,
CacheTTL: time.Minute, CacheTTL: time.Minute,
} }
@@ -194,5 +202,9 @@ func configDefault(config ...Config) Config {
cfg.Reset = true cfg.Reset = true
} }
if userConfig.DisableStartupCheck {
cfg.DisableStartupCheck = true
}
return cfg return cfg
} }

View File

@@ -11,7 +11,8 @@ var cacheTTL = time.Second
// Storage interface that is implemented by storage providers // Storage interface that is implemented by storage providers
type Storage struct { type Storage struct {
db rueidis.Client db rueidis.Client
initErr error
} }
// New creates a new rueidis storage // New creates a new rueidis storage
@@ -45,7 +46,6 @@ func New(config ...Config) *Storage {
} }
} }
// Update config values accordingly and start new Client
db, err := rueidis.NewClient(rueidis.ClientOption{ db, err := rueidis.NewClient(rueidis.ClientOption{
Username: cfg.Username, Username: cfg.Username,
Password: cfg.Password, Password: cfg.Password,
@@ -64,19 +64,25 @@ func New(config ...Config) *Storage {
AlwaysPipelining: cfg.AlwaysPipelining, AlwaysPipelining: cfg.AlwaysPipelining,
}) })
if err != nil { if err != nil {
if cfg.DisableStartupCheck {
return &Storage{initErr: err}
}
panic(err) panic(err)
} }
// Test connection if !cfg.DisableStartupCheck {
if err := db.Do(context.Background(), db.B().Ping().Build()).Error(); err != nil { // Test connection
panic(err) if err := db.Do(context.Background(), db.B().Ping().Build()).Error(); err != nil {
}
// Empty collection if Clear is true
if cfg.Reset {
if err := db.Do(context.Background(), db.B().Flushdb().Build()).Error(); err != nil {
panic(err) panic(err)
} }
// Empty collection if Clear is true
if cfg.Reset {
if err := db.Do(context.Background(), db.B().Flushdb().Build()).Error(); err != nil {
panic(err)
}
}
} }
// Create new store // Create new store
@@ -87,6 +93,9 @@ func New(config ...Config) *Storage {
// GetWithContext gets value by key with context // GetWithContext gets value by key with context
func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) { func (s *Storage) GetWithContext(ctx context.Context, key string) ([]byte, error) {
if s.db == nil {
return nil, s.initErr
}
if len(key) <= 0 { if len(key) <= 0 {
return nil, nil return nil, nil
} }
@@ -104,6 +113,9 @@ func (s *Storage) Get(key string) ([]byte, error) {
// SetWithContext sets key with value with context // SetWithContext sets key with value with context
func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error { func (s *Storage) SetWithContext(ctx context.Context, key string, val []byte, exp time.Duration) error {
if s.db == nil {
return s.initErr
}
if len(key) <= 0 || len(val) <= 0 { if len(key) <= 0 || len(val) <= 0 {
return nil return nil
} }
@@ -121,6 +133,9 @@ func (s *Storage) Set(key string, val []byte, exp time.Duration) error {
// DeleteWithContext deletes key by key with context // DeleteWithContext deletes key by key with context
func (s *Storage) DeleteWithContext(ctx context.Context, key string) error { func (s *Storage) DeleteWithContext(ctx context.Context, key string) error {
if s.db == nil {
return s.initErr
}
if len(key) <= 0 { if len(key) <= 0 {
return nil return nil
} }
@@ -134,6 +149,9 @@ func (s *Storage) Delete(key string) error {
// ResetWithContext resets all keys with context // ResetWithContext resets all keys with context
func (s *Storage) ResetWithContext(ctx context.Context) error { func (s *Storage) ResetWithContext(ctx context.Context) error {
if s.db == nil {
return s.initErr
}
return s.db.Do(ctx, s.db.B().Flushdb().Build()).Error() return s.db.Do(ctx, s.db.B().Flushdb().Build()).Error()
} }
@@ -144,6 +162,10 @@ func (s *Storage) Reset() error {
// Close the database // Close the database
func (s *Storage) Close() error { func (s *Storage) Close() error {
if s.db == nil {
return nil
}
s.db.Close() s.db.Close()
return nil return nil
} }

View File

@@ -49,6 +49,17 @@ func newTestStore(t testing.TB, opts ...testredis.Option) *Storage {
return New(newConfigFromContainer(t, opts...)) return New(newConfigFromContainer(t, opts...))
} }
func Test_Rueidis_New_DisableStartupCheck(t *testing.T) {
require.NotPanics(t, func() {
store := New(Config{
InitAddress: []string{"127.0.0.1:6390"},
DisableStartupCheck: true,
})
require.NotNil(t, store)
require.NoError(t, store.Close())
})
}
func Test_Rueidis_Set(t *testing.T) { func Test_Rueidis_Set(t *testing.T) {
var ( var (
key = "john" key = "john"