Skip to content

Commit

Permalink
feat: use connection pooling
Browse files Browse the repository at this point in the history
Using a single connection is not thread-safe and should not be used in
applications like this where several requests may be processed at a
time.

This also makes it more reliable. Previously the db connection could not
recover from a DB server restart. With connection pooling it is now able
to recover. May not be perfect but is good enough atm.

Closes #24.
  • Loading branch information
crazybolillo committed Sep 28, 2024
1 parent 7326e19 commit 450b951
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 30 deletions.
11 changes: 5 additions & 6 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ package main
import (
"context"
"fmt"
"github.com/crazybolillo/eryth/internal/bouncer"
"github.com/crazybolillo/eryth/internal/handler"
"github.com/crazybolillo/eryth/internal/service"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/httplog/v2"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"log/slog"
"net/http"
"net/url"
Expand Down Expand Up @@ -53,12 +52,12 @@ func serve(ctx context.Context) error {
slog.String("database", u.Path[1:]),
)

conn, err := pgx.Connect(ctx, os.Getenv("DATABASE_URL"))
pool, err := pgxpool.New(ctx, os.Getenv("DATABASE_URL"))
if err != nil {
slog.Error("failed to establish database connection", slog.String("reason", err.Error()))
return err
}
defer conn.Close(ctx)
defer pool.Close()

r := chi.NewRouter()
r.Use(httplog.RequestLogger(httplog.NewLogger("eryth", httplog.Options{
Expand All @@ -68,10 +67,10 @@ func serve(ctx context.Context) error {
})))
r.Use(middleware.AllowContentEncoding("application/json"))

endpoint := handler.Endpoint{Service: &service.EndpointService{Cursor: conn}}
endpoint := handler.Endpoint{Service: &service.EndpointService{Cursor: pool}}
r.Mount("/endpoints", endpoint.Router())

checker := &bouncer.Bouncer{Conn: conn}
checker := &service.Bouncer{Cursor: pool}
authorization := handler.Authorization{Bouncer: checker}
r.Mount("/bouncer", authorization.Router())

Expand Down
18 changes: 9 additions & 9 deletions docs/swagger.yaml
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
definitions:
bouncer.Response:
handler.AuthorizationRequest:
properties:
allow:
type: boolean
callerid:
endpoint:
type: string
destination:
extension:
type: string
type: object
handler.AuthorizationRequest:
model.BouncerResponse:
properties:
endpoint:
allow:
type: boolean
callerid:
type: string
extension:
destination:
type: string
type: object
model.Endpoint:
Expand Down Expand Up @@ -128,7 +128,7 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/bouncer.Response'
$ref: '#/definitions/model.BouncerResponse'
"400":
description: Bad Request
"500":
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ require (
require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/text v0.14.0 // indirect
)
6 changes: 3 additions & 3 deletions internal/handler/authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ package handler
import (
"context"
"encoding/json"
"github.com/crazybolillo/eryth/internal/bouncer"
"github.com/crazybolillo/eryth/internal/model"
"github.com/go-chi/chi/v5"
"log/slog"
"net/http"
)

type CallBouncer interface {
Check(ctx context.Context, endpoint, dialed string) bouncer.Response
Check(ctx context.Context, endpoint, dialed string) model.BouncerResponse
}

type Authorization struct {
Expand All @@ -34,7 +34,7 @@ func (e *Authorization) Router() chi.Router {
// @Accept json
// @Produce json
// @Param payload body AuthorizationRequest true "Action to be reviewed"
// @Success 200 {object} bouncer.Response
// @Success 200 {object} model.BouncerResponse
// @Failure 400
// @Failure 500
// @Tags bouncer
Expand Down
7 changes: 7 additions & 0 deletions internal/model/bouncer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package model

type BouncerResponse struct {
Allow bool `json:"allow"`
Destination string `json:"destination"`
CallerID string `json:"callerid"`
}
18 changes: 6 additions & 12 deletions internal/bouncer/bouncer.go → internal/service/bouncer.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
package bouncer
package service

import (
"context"
"github.com/crazybolillo/eryth/internal/db"
"github.com/crazybolillo/eryth/internal/model"
"github.com/crazybolillo/eryth/internal/sqlc"
"github.com/jackc/pgx/v5"
"log/slog"
)

type Response struct {
Allow bool `json:"allow"`
Destination string `json:"destination"`
CallerID string `json:"callerid"`
}

type Bouncer struct {
*pgx.Conn
Cursor
}

func (b *Bouncer) Check(ctx context.Context, endpoint, dialed string) Response {
result := Response{
func (b *Bouncer) Check(ctx context.Context, endpoint, dialed string) model.BouncerResponse {
result := model.BouncerResponse{
Allow: false,
}

Expand All @@ -44,7 +38,7 @@ func (b *Bouncer) Check(ctx context.Context, endpoint, dialed string) Response {
return result
}

return Response{
return model.BouncerResponse{
Allow: true,
Destination: row.ID,
CallerID: row.Callerid.String,
Expand Down

0 comments on commit 450b951

Please sign in to comment.