mirror of
https://github.com/tiennm99/keepalive.git
synced 2026-10-11 03:13:31 +00:00
- Generated service names come only from a URL host, a key=value DSN's host=, or a MySQL tcp() address, so a password in a key=value DSN can no longer end up in the name printed on every log line. - connect_timeout is added only to postgres:// and postgresql:// URLs (parsed, so it never lands after a fragment) or as a key=value token, never glued onto a key=value value containing "://". - Driver parse errors that quote password fragments (mongo escape errors, lib/pq's missing "=" error) are replaced with generic hints. - MongoDB keeps its client only after a successful connect, so a failed connect is not disconnected twice. - Couchbase gets ready_timeout plus 1 minute to connect, so raising ready_timeout takes effect. - Shutdown waits at most 7 seconds, inside Docker's 10-second grace period. - Each adapter declares its config keys; unknown keys anywhere in the file, including under config, log a warning without blocking start.
104 lines
2.6 KiB
Go
104 lines
2.6 KiB
Go
// Package adapter defines the contract every database driver implements.
|
|
//
|
|
// An Adapter connects to a single backing store, increments a counter on every
|
|
// keepalive tick (the cheapest write that proves the cluster is live), and
|
|
// releases its resources on Close. New databases plug in by adding a file in
|
|
// this package and registering a factory in Registry.
|
|
package adapter
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Adapter interface {
|
|
Connect(ctx context.Context) error
|
|
Increment(ctx context.Context) (int64, error)
|
|
Close(ctx context.Context) error
|
|
}
|
|
|
|
type Config map[string]string
|
|
|
|
func (c Config) Required(name string) (string, error) {
|
|
v, ok := c[name]
|
|
if !ok || v == "" {
|
|
return "", fmt.Errorf("config %s is required", name)
|
|
}
|
|
return v, nil
|
|
}
|
|
|
|
func (c Config) Optional(name, def string) string {
|
|
if v, ok := c[name]; ok && v != "" {
|
|
return v
|
|
}
|
|
return def
|
|
}
|
|
|
|
func (c Config) OptionalDuration(name string, def time.Duration) (time.Duration, error) {
|
|
value := strings.TrimSpace(c[name])
|
|
if value == "" {
|
|
return def, nil
|
|
}
|
|
if d, err := time.ParseDuration(value); err == nil {
|
|
if d <= 0 {
|
|
return 0, fmt.Errorf("config %s must be greater than zero", name)
|
|
}
|
|
return d, nil
|
|
}
|
|
seconds, err := strconv.Atoi(value)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("config %s must be a duration like 30s or an integer number of seconds", name)
|
|
}
|
|
d := time.Duration(seconds) * time.Second
|
|
if d <= 0 {
|
|
return 0, fmt.Errorf("config %s must be greater than zero", name)
|
|
}
|
|
return d, nil
|
|
}
|
|
|
|
func (c Config) OptionalUint64(name string, def uint64) (uint64, error) {
|
|
value := strings.TrimSpace(c[name])
|
|
if value == "" {
|
|
return def, nil
|
|
}
|
|
out, err := strconv.ParseUint(value, 10, 64)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("config %s must be an unsigned integer", name)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
type Factory func(Config) (Adapter, error)
|
|
|
|
var Registry = map[string]Factory{}
|
|
|
|
// ConfigKeys lists the config keys each adapter reads, so the config loader
|
|
// can warn about misspelled or unsupported keys. counter_key is set by the
|
|
// loader and is not listed.
|
|
var ConfigKeys = map[string][]string{}
|
|
|
|
// ConnectTimeouter is implemented by adapters whose Connect may legitimately
|
|
// need longer than the runner's default connect timeout.
|
|
type ConnectTimeouter interface {
|
|
ConnectTimeout() time.Duration
|
|
}
|
|
|
|
func New(adapterType string, cfg Config) (Adapter, error) {
|
|
f, ok := Registry[adapterType]
|
|
if !ok {
|
|
return nil, fmt.Errorf("unknown adapter %q (known: %v)", adapterType, Known())
|
|
}
|
|
return f(cfg)
|
|
}
|
|
|
|
func Known() []string {
|
|
out := make([]string, 0, len(Registry))
|
|
for k := range Registry {
|
|
out = append(out, k)
|
|
}
|
|
return out
|
|
}
|