Files
keepalive/docs/adding-an-adapter.md
tiennm99 e445b5cbc5 docs: move reference material from the README into docs
The README now covers what keepalive is, a quick start, the adapter table,
and links. Reference material moves to docs/: configuration (every option,
per-adapter keys, validation), deployment (Compose, Coolify, Docker, source),
how it works (writes, retries, timeouts, logging), known issues (PostgreSQL
public-schema permissions and Aiven's ACL key-pattern tilde, by log line),
and adding an adapter (now including ConfigKeys registration).

.gitignore also excludes config.yaml, matching .dockerignore.
2026-10-09 12:37:52 +07:00

2.1 KiB

Adding an adapter

An adapter connects to one datastore, increments a counter on every tick, and releases its resources on close. Adapters live in the adapter package, one file each.

  1. Create adapter/<name>.go and implement the Adapter interface from adapter/adapter.go:

    type Adapter interface {
    	Connect(ctx context.Context) error           // open the client and create the counter if missing
    	Increment(ctx context.Context) (int64, error) // add 1 and return the new value
    	Close(ctx context.Context) error              // release resources; called after every session
    }
    
  2. Register the factory and its config keys in init(). The factory only reads config and must not do network I/O, because keepalive calls it at startup to validate the file.

    func init() {
    	ConfigKeys["<name>"] = []string{"url"}
    	Registry["<name>"] = func(cfg Config) (Adapter, error) {
    		url, err := cfg.Required("url")
    		if err != nil {
    			return nil, err
    		}
    		return &myAdapter{url: url, key: cfg.Optional("counter_key", "counter")}, nil
    	}
    }
    

    ConfigKeys drives the unknown-key warnings; a test fails if an adapter is registered without it. counter_key is supplied by the config loader and is not listed.

  3. Follow the conventions that keep the runner's guarantees:

    • Honour ctx in Connect and Increment; the runner bounds both.
    • Store the client on the adapter only after Connect succeeds, so Close never closes a failed client twice.
    • Make Connect idempotent: it re-runs after every failure and should recreate a missing counter.
    • Implement ConnectTimeout() time.Duration only if setup legitimately needs longer than 1 minute.
    • Keep the driver pure Go (CGO_ENABLED=0).
  4. Document it: add the adapter to config.example.yml, its keys to Configuration, its writes to How it works, and a row to the README's adapter table.

  5. Test it: go vet ./... and go test -race ./....