chore(users): remove coordinator leftovers, tidy for userservice

- delete api/requests.http and ARCHITECTURE.md (coordinator content)
- rewrite README.md and .env.example for the userservice
- drop dead RunPeriodic (reaper machinery userservice has no use for)
- fix .gitignore/.dockerignore/.golangci.yml module + artifact names
- degeneralize stale copied comments that said "coordinator"
This commit is contained in:
Efremenko Arhip
2026-07-26 16:36:17 +03:00
parent 67407220c3
commit 0c1f5f06d4
11 changed files with 68 additions and 648 deletions
+1 -1
View File
@@ -8,6 +8,6 @@ type System struct{}
func NewClock() System { return System{} }
// Now returns UTC so every timestamp the coordinator writes is comparable
// Now returns UTC so every timestamp this service writes is comparable
// regardless of the host's timezone.
func (System) Now() time.Time { return time.Now().UTC() }
+1 -1
View File
@@ -25,7 +25,7 @@ func NewPool(ctx context.Context, cfg Config, log *slog.Logger) (*pgxpool.Pool,
}
// pgxpool.New is lazy, so a ping is needed to actually reach the server.
// It is retried because at startup — especially under docker-compose, where
// the coordinator can boot before Postgres is accepting connections — a
// this service can boot before Postgres is accepting connections — a
// service should wait for its database rather than crash-loop.
if err := pingWithRetry(ctx, pool, cfg.DBConnectTimeout, log); err != nil {
pool.Close()
+2 -32
View File
@@ -1,5 +1,4 @@
// Server: the HTTP listener and the background lease reaper, both shut down
// cleanly on a signal.
// Server: the HTTP listener, shut down cleanly on a signal.
package infra
import (
@@ -12,7 +11,7 @@ import (
const shutdownGrace = 15 * time.Second
// Run serves handler until ctx is cancelled, then drains in-flight requests.
// RunServer serves handler until ctx is cancelled, then drains in-flight requests.
func RunServer(ctx context.Context, log *slog.Logger, addr string, handler http.Handler) error {
srv := &http.Server{
Addr: addr,
@@ -43,32 +42,3 @@ func RunServer(ctx context.Context, log *slog.Logger, addr string, handler http.
defer cancel()
return srv.Shutdown(shutdownCtx)
}
// RunReaper periodically reclaims tasks whose lease elapsed, so a worker that
// died without a heartbeat cannot strand its task in 'leased' forever.
// RunPeriodic invokes fn on an interval until ctx is done, logging how many rows
// each tick affected. It backs the background reapers (expired leases, offline
// workers) — each is a set-based UPDATE that is safe to run repeatedly and
// concurrently across coordinators.
func RunPeriodic(ctx context.Context, log *slog.Logger, name string, interval time.Duration,
fn func(context.Context) (int64, error)) {
t := time.NewTicker(interval)
defer t.Stop()
for {
select {
case <-ctx.Done():
return
case <-t.C:
n, err := fn(ctx)
if err != nil {
log.Debug(name+" skipped", "err", err)
continue
}
if n > 0 {
log.Info(name, "count", n)
}
}
}
}
+3 -3
View File
@@ -9,8 +9,8 @@ import (
"github.com/jackc/pgx/v5/pgconn"
)
// Transient PostgreSQL failures. Under concurrent claiming these are expected
// rather than exceptional: two coordinators touching neighbouring rows can
// Transient PostgreSQL failures. Under concurrent writes these are expected
// rather than exceptional: two service instances touching neighbouring rows can
// deadlock or fail to serialize, and the correct response is to try again.
const (
codeSerializationFailure = "40001"
@@ -60,7 +60,7 @@ func isTransient(err error) bool {
// withRetry runs op, retrying only transient database failures with
// exponential backoff and jitter, and giving up as soon as ctx is done.
//
// Jitter matters here: without it, several coordinators that collide once will
// Jitter matters here: without it, several instances that collide once will
// retry in lockstep and collide again at exactly the same moment.
func withRetry(ctx context.Context, op func(context.Context) error) error {
b := backoff.NewExponentialBackOff()