feat: backend database connection #3
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "0-backend-database-connection"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Key Features
GET /health): Reports database connectivity statusGET /api/employees): Returns employee ID and nameGET /api/invoices): Query invoices by date range and employee with 500-result limitFile:
backend/internal/database/database.goIssue: background reconnect goroutine never stops and
Close()does not signal it. This can race with connection/closure and leak goroutines when the server shuts down.Request:
NewManageraccept acontext.Contextor add an internal stop channel soconnectLooplistens for cancellation (select { case <-ctx.Done(): return }).Close()cancels that context / closes the stop channel and waits for the goroutine to exit (e.g., viasync.WaitGroupor a done channel) before returning.Rationale: without this,
connectLoopcan reopen connections while shutdown is in progress and leave goroutines running afterClose()returns.File:
backend/internal/database/database.goIssue:
initializeSchemaalways inserts a new row intohealth_checkon each successful connection attempt. This will grow the table indefinitely with one row per reconnect.Request:
MERGE/UPSERTto update a single health row, orDELETE+INSERTorTRUNCATEif you explicitly want a single latest row.Rationale: uncontrolled growth may cause the table to balloon over time and affect backups/maintenance.
File:
backend/internal/database/database.goIssue: Connection strings use
encrypt=disable. That may be okay for internal networks but should be explicit and configurable.Request:
Encrypt bool/EncryptMode stringinConfig) and default to secure settings.Rationale: Disabling encryption by default is a security risk if this ever runs outside a trusted network.
Changes Requested
Please address the following high-impact items before merging:
Managerlifecycle cancellable and deterministic: ensure the background reconnect goroutine can be stopped andClose()waits for it to exit (use a context the manager owns or an internal stop channel +sync.WaitGroup).health_check: changeinitializeExtensionsSchemato upsert/update a single health row (or insert-only-if-empty / truncate) instead of inserting on every reconnect.encrypt=disable; add config fields (e.g.,EncryptMode/Encrypt+TrustServerCertificate) and update.env.example.verifyReadOnlydeterministic: perform the write-check on a single connection (usedb.BeginTxordb.Conn) so create/drop happen on the same physical connection and are rolled back.start_date/end_dateintotime.Timeand pass typed values to the driver (not raw strings).argsslice order always match (avoid ad-hoc@p1/@p3mismatch).backend/go.modduplicaterequireblocks and rungo mod tidy.Summary of Changes
backend/internal/database/database.go.backend/cmd/server/main.gonow buildsdatabase.Configand passesdatabase.Managerto server.GET /health,GET /api/employees,GET /api/invoicesinbackend/internal/server/server.go..env.example,README.md,justfile, andspecs/000-proof-of-concept.mdfor DB setup and dev flow.backend/go.mod/go.sumto add DB driver and dependencies.Overall Feedback
What needs work (negative):
Close()does not wait,initializeExtensionsSchemaalways inserts a new row (table will grow), andverifyReadOnlyuses pooled ops that can be nondeterministic and leave objects behind.argsslice that can get out of sync and sends raw date strings to the DB driver; this risks wrong results and SQL errors.backend/go.modcontains duplicaterequireblocks and should be tidied.What I liked (positive):
Small, targeted fixes in the manager and the invoices handler will make this production-ready. Ping me when you’ve addressed these and I’ll re-review quickly — nice progress! 44d680
File:
backend/internal/database/database.goIssue:
verifyReadOnlyusesCREATE TABLE #read_only_testthen attempts toDROP TABLEif the create succeeds. That check is unreliable when usingdatabase/sqlconnection pooling because the create and drop may run on different physical connections (or the temp table may persist on the connection), leading to false negatives/positives and residual objects.Request:
tx, err := db.BeginTx(ctx, nil)to get a dedicated connection,Execthe create within the tx, thentx.Rollback()so nothing is persisted. This ensures the create/drop happen on the same connection and are rolled back.Rationale: the existing check can behave inconsistently in production and can leave temp objects behind. Using a Tx (or another same-connection mechanism) makes the check deterministic and safe.
File:
backend/internal/database/database.goIssue: Connection strings hardcode
encrypt=disablewhich is insecure if deployed outside a trusted network.Actionable change:
Encrypt(bool) orEncryptMode(string) field toConfigand use it when building connection strings instead of hardcodingencrypt=disable.encrypt=true;trustservercertificate=false) and allow overriding via config.Rationale: disabling encryption by default is a security risk. Make encryption explicit and configurable so deployments can enforce TLS.
@Pertempto
Please stop unconditional INSERTs that will bloat the table. Actionable changes:
SELECT COUNT(*)first), orI suggest adding a single-row key like
name VARCHAR(64) UNIQUEand thenMERGE/UPDATEthetimestampfor key"manager_health". That avoids table growth while keeping a last-seen timestamp.README.md: The SQL for the Extensions DB grantsdb_ddladminto theaptora_extensionsuser. That's highly permissive (can change schema). Please dropdb_ddladminand grant only the roles needed (e.g.,db_datareader,db_datawriter). If schema changes are required, perform them with an admin account as part of deployment, not via the application user.