mirror of
https://github.com/tiennm99/keepalive.git
synced 2026-08-21 22:23:59 +00:00
docs(config): add examples for all adapters
This commit is contained in:
+10
-2
@@ -8,9 +8,17 @@ services:
|
||||
url: redis://default@redis-a.example.com:6379
|
||||
namespace: keepalive
|
||||
|
||||
- adapter: redis
|
||||
- adapter: valkey
|
||||
config:
|
||||
url: redis://default@redis-b.example.com:6379
|
||||
url: valkey://default@valkey-a.example.com:6379
|
||||
|
||||
- adapter: postgresql
|
||||
config:
|
||||
url: postgresql://user:pass@postgres-a.example.com:5432/keepalive?sslmode=require
|
||||
|
||||
- adapter: mysql
|
||||
config:
|
||||
dsn: user:pass@tcp(mysql-a.example.com:3306)/keepalive
|
||||
|
||||
- adapter: mongodb
|
||||
config:
|
||||
|
||||
+8
-2
@@ -33,8 +33,14 @@ func TestConfigExampleYMLParses(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("loadConfigFile returned error: %v", err)
|
||||
}
|
||||
if len(services) != 4 {
|
||||
t.Fatalf("len(services) = %d, want 4", len(services))
|
||||
wantAdapters := []string{"redis", "valkey", "postgresql", "mysql", "mongodb", "couchbase"}
|
||||
if len(services) != len(wantAdapters) {
|
||||
t.Fatalf("len(services) = %d, want %d", len(services), len(wantAdapters))
|
||||
}
|
||||
for i, want := range wantAdapters {
|
||||
if services[i].AdapterType != want {
|
||||
t.Fatalf("services[%d].AdapterType = %q, want %q", i, services[i].AdapterType, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
---
|
||||
type: research-report
|
||||
topic: supabase-postgres-fit
|
||||
created_at: 2026-06-28T23:35:00Z
|
||||
project: keepalive
|
||||
---
|
||||
|
||||
# Research Report: Supabase Postgres Fit
|
||||
|
||||
## Summary
|
||||
|
||||
Yes. This project can apply to Supabase Postgres now through the existing `postgresql` adapter. Supabase exposes a normal Postgres connection string, and this repo already uses `database/sql` with `github.com/lib/pq`, which accepts Postgres URL DSNs and `sslmode` parameters.
|
||||
|
||||
No source-code change required for basic use. Need only add a Supabase service config with the right connection string. For production polish, add a Supabase example, set Postgres max open connections to `1`, and consider configurable schema/table to avoid creating `public.keepalive`.
|
||||
|
||||
## Research Methodology
|
||||
|
||||
- Sources consulted: local repo files + 4 official/external docs.
|
||||
- Date: 2026-06-28.
|
||||
- Key terms: Supabase Postgres connection string, Supabase pooler, Supavisor, lib/pq DSN, Supabase RLS public schema.
|
||||
- Scope: compatibility, config, security, performance. No migration or implementation.
|
||||
|
||||
## Key Findings
|
||||
|
||||
### 1. Current Project Fit
|
||||
|
||||
Current adapter: [adapter/postgresql.go](/config/workspace/tiennm99/keepalive/adapter/postgresql.go)
|
||||
|
||||
Behavior:
|
||||
|
||||
- Accepts `config.url`.
|
||||
- Supports adapter names `postgresql` and `postgres`.
|
||||
- Opens `sql.Open("postgres", url)` via `github.com/lib/pq`.
|
||||
- On connect: `Ping`, `CREATE TABLE IF NOT EXISTS keepalive`, seed key.
|
||||
- On tick: transaction + `UPDATE keepalive SET value = value + 1 WHERE key = $1 RETURNING value`.
|
||||
|
||||
Supabase fit:
|
||||
|
||||
| Requirement | Status |
|
||||
| --- | --- |
|
||||
| Postgres protocol | Supported |
|
||||
| URL DSN | Supported by `lib/pq` |
|
||||
| SSL | Supported via `sslmode` |
|
||||
| Cheap write | Supported |
|
||||
| Reconnect after startup failure | Supported |
|
||||
| Supabase-specific API | Not needed |
|
||||
|
||||
### 2. Supabase Connection Choice
|
||||
|
||||
Supabase docs say Postgres clients use connection strings. Direct connection is best for long-lived servers, but the direct endpoint is IPv6 unless the project has IPv4 add-on. Shared pooler session mode is for persistent app traffic from IPv4-only networks. Transaction mode is for temporary/serverless clients and does not support prepared statements.
|
||||
|
||||
This daemon is long-lived. Recommended order:
|
||||
|
||||
1. Direct connection if runtime supports IPv6 or Supabase IPv4 add-on enabled.
|
||||
2. Shared pooler session mode if runtime is IPv4-only.
|
||||
3. Transaction pooler only if tested, because Supabase warns it does not support prepared statements.
|
||||
|
||||
Example direct:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
- name: supabase-postgres
|
||||
adapter: postgresql
|
||||
interval: 5m
|
||||
config:
|
||||
url: "postgresql://postgres:[PASSWORD]@db.[PROJECT-REF].supabase.co:5432/postgres?sslmode=require"
|
||||
```
|
||||
|
||||
Example shared pooler session mode:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
- name: supabase-postgres
|
||||
adapter: postgresql
|
||||
interval: 5m
|
||||
config:
|
||||
url: "postgresql://postgres.[PROJECT-REF]:[PASSWORD]@aws-[REGION].pooler.supabase.com:5432/postgres?sslmode=require"
|
||||
```
|
||||
|
||||
### 3. Security Considerations
|
||||
|
||||
Current SQL creates unqualified table `keepalive`. With a normal Supabase connection, that likely means `public.keepalive`.
|
||||
|
||||
Supabase docs say RLS must be enabled for tables in exposed schemas, and `public` is exposed by default. Tables created via raw SQL do not automatically get RLS unless configured.
|
||||
|
||||
Safer options:
|
||||
|
||||
1. Use as-is, then enable RLS on `public.keepalive` and add no API policies.
|
||||
2. Use a private schema through connection `search_path` if compatible with chosen pooler.
|
||||
3. Better code hardening: add optional `schema` / `table` config and qualify identifiers safely.
|
||||
|
||||
Runtime role:
|
||||
|
||||
- Basic config with `postgres` user works.
|
||||
- Least-privilege role is not ideal today because adapter always runs `CREATE TABLE IF NOT EXISTS`; it needs create/init permissions at startup.
|
||||
- Hardening option: add `init: false` mode after manually creating table, then grant only `SELECT`, `INSERT`, `UPDATE`.
|
||||
|
||||
### 4. Performance Insights
|
||||
|
||||
Load is tiny.
|
||||
|
||||
At default `interval: 1m`, each Supabase service does about:
|
||||
|
||||
- 1 update/minute
|
||||
- 1,440 updates/day
|
||||
- 43,200 updates/month
|
||||
|
||||
This is negligible for Postgres. It still keeps one Go `sql.DB` open. For Supabase small/free tiers, set `db.SetMaxOpenConns(1)` in the adapter to make connection usage explicit.
|
||||
|
||||
### 5. Free Project Pausing
|
||||
|
||||
Supabase docs currently state Free Plan projects with low activity over a 7-day period may be paused, while paid projects are not paused for inactivity.
|
||||
|
||||
This daemon performs real database writes, so it is technically aligned with a keepalive use case. Do not treat this as SLA. If the project matters, paid Supabase plan is cleaner than relying on keepalive activity.
|
||||
|
||||
## Comparative Analysis
|
||||
|
||||
| Option | Pros | Cons | Recommendation |
|
||||
| --- | --- | --- | --- |
|
||||
| Existing `postgresql` adapter | Works now, simple | Creates `keepalive` in default schema | Use now |
|
||||
| New `supabase` adapter alias | Friendlier UX | Mostly duplicate of Postgres adapter | Optional only |
|
||||
| Add Supabase docs/example | Low risk, useful | No behavior change | Do it |
|
||||
| Add schema/table config | Better security posture | Needs safe identifier handling/tests | Good next hardening |
|
||||
| Use Supabase Data API | Avoid DB conn details | Wrong tool for daemon counter writes | Do not do |
|
||||
|
||||
## Implementation Recommendations
|
||||
|
||||
### Quick Start
|
||||
|
||||
1. In Supabase dashboard, open project -> Connect.
|
||||
2. Copy direct or session pooler connection string.
|
||||
3. Add `?sslmode=require` unless already present.
|
||||
4. Add service config:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
- name: supabase-postgres
|
||||
adapter: postgresql
|
||||
interval: 5m
|
||||
counter_key: supabase-postgres
|
||||
config:
|
||||
url: "postgresql://postgres.[PROJECT-REF]:[PASSWORD]@aws-[REGION].pooler.supabase.com:5432/postgres?sslmode=require"
|
||||
```
|
||||
|
||||
5. Run:
|
||||
|
||||
```bash
|
||||
go run .
|
||||
```
|
||||
|
||||
6. Confirm in Supabase SQL editor:
|
||||
|
||||
```sql
|
||||
select * from keepalive;
|
||||
```
|
||||
|
||||
### Optional Code Changes
|
||||
|
||||
Minimal docs-only improvement:
|
||||
|
||||
- Add Supabase example to `config.example.yml`.
|
||||
- Add README note: direct vs session pooler.
|
||||
|
||||
Small hardening:
|
||||
|
||||
- In `adapter/postgresql.go`, after `sql.Open`, set:
|
||||
|
||||
```go
|
||||
db.SetMaxOpenConns(1)
|
||||
db.SetMaxIdleConns(1)
|
||||
```
|
||||
|
||||
Production hardening:
|
||||
|
||||
- Add optional `schema` and `table` config.
|
||||
- Validate identifiers with strict allow-list.
|
||||
- Quote identifiers safely.
|
||||
- Add tests for default table and custom schema/table SQL.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- IPv4-only deploy using direct Supabase host without IPv4 add-on: connection fails. Use shared pooler session mode.
|
||||
- Transaction pooler with prepared statements: Supabase warns this is unsupported. Prefer direct/session mode for this daemon.
|
||||
- Raw SQL table in `public`: enable RLS or use private schema.
|
||||
- Password special chars in URL: URL-encode password.
|
||||
- Custom least-privilege role: current adapter still needs init permissions.
|
||||
|
||||
## Verification
|
||||
|
||||
Local tests:
|
||||
|
||||
```text
|
||||
go test ./...
|
||||
ok github.com/tiennm99/keepalive
|
||||
ok github.com/tiennm99/keepalive/adapter
|
||||
```
|
||||
|
||||
## Resources & References
|
||||
|
||||
- Supabase connect docs: https://supabase.com/docs/guides/database/connecting-to-postgres
|
||||
- Supabase RLS docs: https://supabase.com/docs/guides/database/postgres/row-level-security
|
||||
- Supabase free project pausing docs: https://supabase.com/docs/guides/platform/free-project-pausing
|
||||
- lib/pq docs: https://pkg.go.dev/github.com/lib/pq
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Use existing `postgresql` adapter with Supabase session pooler or direct URL.
|
||||
2. Add README/config example for Supabase.
|
||||
3. Add `SetMaxOpenConns(1)` hardening.
|
||||
4. Decide whether `public.keepalive` acceptable; if not, implement schema/table config.
|
||||
|
||||
## Unresolved Questions
|
||||
|
||||
- Target deployment supports IPv6, or IPv4-only?
|
||||
- Is `public.keepalive` acceptable, or need private schema by default?
|
||||
Reference in New Issue
Block a user