mirror of
https://github.com/tiennm99/keepalive.git
synced 2026-10-11 12:19:02 +00:00
125 lines
5.0 KiB
Markdown
125 lines
5.0 KiB
Markdown
# keepalive
|
|
|
|
Pluggable Go daemon that periodically touches external services to prevent idle shutdowns, pauses, or cold starts.
|
|
|
|
The current adapters perform cheap datastore writes for Redis Cloud, Valkey, Aiven, Neon, Supabase, MongoDB Atlas, Couchbase Capella, and similar hosted services.
|
|
|
|
Successor to the `*-keepalive` family: one binary, one image, six datastore adapters.
|
|
|
|
## Configuration
|
|
|
|
By default, keepalive reads the first config file it finds: `config.yml`, `config.yaml`, `/config.yml`, then `/config.yaml`. One deployment can keep any number of services alive.
|
|
|
|
```yaml
|
|
interval: 1m
|
|
counter_key: counter
|
|
|
|
services:
|
|
- adapter: redis
|
|
config:
|
|
url: redis://default@redis-a.example.com:6379
|
|
|
|
- adapter: mongodb
|
|
config:
|
|
uri: mongodb+srv://user:pass@mongo-a.example.com
|
|
database: keepalive
|
|
collection: counter
|
|
|
|
- adapter: couchbase
|
|
config:
|
|
connection_string: couchbases://couchbase-a.example.com
|
|
username: user
|
|
password: pass
|
|
bucket_name: keepalive
|
|
scope_name: _default
|
|
collection_name: _default
|
|
```
|
|
|
|
`name` is optional. When omitted, keepalive generates a name from `adapter` and the connection host, such as `redis-redis-a-example-com`. Duplicate generated names get suffixes like `redis-redis-a-example-com-2`.
|
|
|
|
`interval` and `counter_key` can be set globally or per service. Per-service values override global values.
|
|
|
|
## Supported adapters
|
|
|
|
| `adapter` | Driver | `config` keys |
|
|
| ------------ | ----------------------------------- | ------------- |
|
|
| `redis` | `github.com/redis/go-redis/v9` | `url` |
|
|
| `valkey` | `github.com/valkey-io/valkey-go` | `url` |
|
|
| `postgresql` | `github.com/lib/pq` | `url` |
|
|
| `mysql` | `github.com/go-sql-driver/mysql` | `dsn` |
|
|
| `mongodb` | `go.mongodb.org/mongo-driver/v2` | `uri`, `database`, `collection` |
|
|
| `couchbase` | `github.com/couchbase/gocb/v2` | `connection_string`, `username`, `password`, `bucket_name`, `scope_name`, `collection_name`, optional `ready_timeout`, optional `bucket_ram_quota_mb` |
|
|
|
|
## Quick start (Compose)
|
|
|
|
```bash
|
|
cp config.example.yml config.yml
|
|
docker compose up -d --build
|
|
```
|
|
|
|
## Quick start (Docker)
|
|
|
|
```bash
|
|
docker build -t keepalive:local .
|
|
docker run -d --name keepalive --restart unless-stopped \
|
|
-v "$PWD/config.yml:/config.yml:ro" \
|
|
keepalive:local
|
|
```
|
|
|
|
If you prefer to mount the config into a working directory instead of the container root, set the container working directory and mount the file there:
|
|
|
|
```bash
|
|
docker run -d --name keepalive --restart unless-stopped \
|
|
--workdir /workspace \
|
|
-v "$PWD/config.yml:/workspace/config.yml:ro" \
|
|
keepalive:local
|
|
```
|
|
|
|
## Quick start (local)
|
|
|
|
```bash
|
|
git clone https://github.com/tiennm99/keepalive
|
|
cd keepalive
|
|
cp config.example.yml config.yml
|
|
go run .
|
|
```
|
|
|
|
## How it works
|
|
|
|
On startup each adapter initializes the minimum resource it owns, then every tick performs the cheapest write that proves the cluster is alive. `counter_key` selects the key/doc ID and defaults to `counter`.
|
|
|
|
- **Redis/Valkey** — initialize with `SETNX key 0`, then `INCR key`
|
|
- **PostgreSQL** — `CREATE TABLE IF NOT EXISTS keepalive`, seed `key`, then `UPDATE ... RETURNING`
|
|
- **MySQL** — `CREATE TABLE IF NOT EXISTS keepalive`, seed `key`, then `UPDATE` + `SELECT`
|
|
- **MongoDB** — upsert `{_id: key, count: 0}` on connect, then `FindOneAndUpdate({_id: key}, {$inc: {count: 1}}, upsert)`
|
|
- **Couchbase** — optionally create the bucket when `bucket_ram_quota_mb` is set, create configured scope/collection when missing, insert `key = 0` if missing, then `GET key` -> `++` -> `UPSERT key`
|
|
|
|
Each configured service starts independently. If one service cannot connect, it logs the error and retries without stopping other services in the same deployment.
|
|
|
|
For hosted Couchbase/Capella clusters, `ready_timeout` defaults to `2m`. If Couchbase still reports `CONNECTION_ERROR`, check the connection string, bucket name, database user permissions, and Capella allowed IP/network access.
|
|
|
|
## Adding a new adapter
|
|
|
|
1. Create `adapter/<name>.go`.
|
|
2. Implement the `Adapter` interface in `adapter/adapter.go` (`Connect`, `Increment`, `Close`).
|
|
3. Register the factory in `init()`:
|
|
```go
|
|
func init() { Registry["<name>"] = func(cfg Config) (Adapter, error) { return &myAdapter{}, nil } }
|
|
```
|
|
4. Add an `import _ "your driver"` if needed, and the adapter config keys to `config.example.yml` and the table above.
|
|
|
|
## Migrated from
|
|
|
|
This repo replaces six single-datastore repos. All are archived with a pointer here:
|
|
|
|
- [redis-keepalive](https://github.com/tiennm99/redis-keepalive)
|
|
- [valkey-keepalive](https://github.com/tiennm99/valkey-keepalive)
|
|
- [postgresql-keepalive](https://github.com/tiennm99/postgresql-keepalive)
|
|
- [mysql-keepalive](https://github.com/tiennm99/mysql-keepalive)
|
|
- [mongodb-keepalive](https://github.com/tiennm99/mongodb-keepalive)
|
|
- [couchbase-keepalive](https://github.com/tiennm99/couchbase-keepalive)
|
|
|
|
## License
|
|
|
|
Apache-2.0 — see [LICENSE](LICENSE).
|