mirror of
https://github.com/hhftechnology/middleware-manager.git
synced 2026-07-17 03:48:24 +00:00
Add robust error checks and helpers, improve transaction safety, and refine default templates handling. - Add api/handlers/test_helpers_test.go with mustUnmarshalResponse and mustScan helpers and update many handler tests to use them or to check json.Unmarshal errors to fail fast. - Replace direct DB Scan calls in tests with mustScan or explicit error checks to surface query failures. - Improve transaction rollback handling across codepaths (config handlers, database migrations, transaction utils, CertGenerator) to log rollback errors and avoid silent failures. - Make WithTransaction/WithTimeoutTransaction/BatchTransaction formatting consistent and add panic-safe rollback logging. - In main, log a warning instead of ignoring EnsureDefaultDataSources errors. - Add/cleanup various helper blank assignments and small refactors in services (config_proxy, config_generator) and switch tests to shared response writers (writeJSONResponse/writeResponseBody) where applicable. - Large cleanup and enhancements in config/defaults.go to preserve string formatting for YAML templates, ensure special fields are quoted/preserved, and tidy whitespace/formatting. These changes improve test reliability, make DB/tx failures more visible, and ensure default template YAML preserves important string values.
85 lines
2.2 KiB
Go
85 lines
2.2 KiB
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
)
|
|
|
|
// TxFn represents a function that uses a transaction
|
|
type TxFn func(*sql.Tx) error
|
|
|
|
// WithTransaction wraps a function with a transaction
|
|
func (db *DB) WithTransaction(fn TxFn) error {
|
|
tx, err := db.Begin()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to begin transaction: %w", err)
|
|
}
|
|
|
|
defer func() {
|
|
if p := recover(); p != nil {
|
|
// Ensure rollback on panic
|
|
log.Printf("Recovered from panic in transaction: %v", p)
|
|
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
|
log.Printf("Warning: Rollback failed after panic: %v", rollbackErr)
|
|
}
|
|
panic(p) // Re-throw panic after rollback
|
|
}
|
|
}()
|
|
|
|
if err := fn(tx); err != nil {
|
|
if rbErr := tx.Rollback(); rbErr != nil {
|
|
log.Printf("Warning: Rollback failed: %v (original error: %v)", rbErr, err)
|
|
return fmt.Errorf("rollback failed: %v (original error: %w)", rbErr, err)
|
|
}
|
|
log.Printf("Transaction rolled back due to error: %v", err)
|
|
return err
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
log.Printf("Error committing transaction: %v", err)
|
|
return fmt.Errorf("commit failed: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// WithTimeoutTransaction wraps a function with a transaction that has a timeout
|
|
func (db *DB) WithTimeoutTransaction(ctx context.Context, timeout time.Duration, fn TxFn) error {
|
|
// Create a context with timeout
|
|
ctx, cancel := context.WithTimeout(ctx, timeout)
|
|
defer cancel()
|
|
|
|
// Create a done channel to signal completion
|
|
done := make(chan error, 1)
|
|
|
|
// Run the transaction in a goroutine
|
|
go func() {
|
|
done <- db.WithTransaction(fn)
|
|
}()
|
|
|
|
// Wait for either context timeout or transaction completion
|
|
select {
|
|
case <-ctx.Done():
|
|
// Context timed out
|
|
return fmt.Errorf("transaction timed out after %v: %w", timeout, ctx.Err())
|
|
case err := <-done:
|
|
// Transaction completed
|
|
return err
|
|
}
|
|
}
|
|
|
|
// BatchTransaction executes multiple operations in a single transaction
|
|
// All operations must succeed or the transaction is rolled back
|
|
func (db *DB) BatchTransaction(operations []TxFn) error {
|
|
return db.WithTransaction(func(tx *sql.Tx) error {
|
|
for i, op := range operations {
|
|
if err := op(tx); err != nil {
|
|
return fmt.Errorf("operation %d failed: %w", i, err)
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|