Skip to content

Commit

Permalink
fix: deadlock retry and record merge
Browse files Browse the repository at this point in the history
  • Loading branch information
yschindel committed Jan 22, 2025
1 parent 25afe7c commit 4f56e1e
Showing 1 changed file with 69 additions and 12 deletions.
81 changes: 69 additions & 12 deletions viz_lca-cost/server/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ package server
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"log"
"os"
"time"

_ "github.com/microsoft/go-mssqldb"
mssql "github.com/microsoft/go-mssqldb"
)

type DBConfig struct {
Expand All @@ -17,9 +21,21 @@ type DBConfig struct {
}

func ConnectDB(config DBConfig) (*sql.DB, error) {
// Build connection string
connString := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%d;database=%s;encrypt=false;trustservercertificate=true;",
config.Server, config.User, config.Password, config.Port, config.Database)
environment := os.Getenv("ENVIRONMENT")

// Build connection string with environment-specific settings
var connString string
if environment == "development" {
// Development connection string with SSL disabled
connString = fmt.Sprintf("server=%s;user id=%s;password=%s;port=%d;database=%s;encrypt=false;trustservercertificate=true;",
config.Server, config.User, config.Password, config.Port, config.Database)
} else {
// Production connection string with SSL enabled
connString = fmt.Sprintf("server=%s;user id=%s;password=%s;port=%d;database=%s;",
config.Server, config.User, config.Password, config.Port, config.Database)
}

log.Printf("Connecting to database in %s mode", environment)

// Create connection pool
db, err := sql.Open("sqlserver", connString)
Expand All @@ -35,7 +51,41 @@ func ConnectDB(config DBConfig) (*sql.DB, error) {

return db, nil
}

func retryOnDeadlock(operation string, fn func() error) error {
maxRetries := 3
for attempt := 0; attempt < maxRetries; attempt++ {
err := fn()
if err == nil {
return nil
}

// Check for SQL Server deadlock error (1205)
if sqlErr, ok := err.(mssql.Error); ok && sqlErr.Number == 1205 {
log.Printf("Deadlock detected during %s (attempt %d of %d), retrying...",
operation, attempt+1, maxRetries)
time.Sleep(time.Millisecond * 100 * time.Duration(attempt+1))
continue
}

return err
}
return fmt.Errorf("failed to %s after %d attempts", operation, maxRetries)
}

func WriteLcaMessage(db *sql.DB, message LcaMessage) error {
return retryOnDeadlock("write lca message", func() error {
return writeLcaMessageWithRetry(db, message)
})
}

func WriteCostMessage(db *sql.DB, message CostMessage) error {
return retryOnDeadlock("write cost message", func() error {
return writeCostMessageWithRetry(db, message)
})
}

func writeCostMessageWithRetry(db *sql.DB, message CostMessage) error {
ctx := context.Background()

tx, err := db.BeginTx(ctx, nil)
Expand All @@ -48,13 +98,12 @@ func WriteCostMessage(db *sql.DB, message CostMessage) error {
MERGE project_data AS target
USING (VALUES (@p1, @p2, @p3, @p4, @p5, @p6, @p7))
AS source (project, filename, timestamp, id, category, cost, cost_unit)
ON target.id = source.id
ON target.id = source.id
AND target.project = source.project
AND target.filename = source.filename
WHEN MATCHED THEN
UPDATE SET
target.project = source.project,
target.filename = source.filename,
target.timestamp = source.timestamp,
target.category = source.category,
target.cost = source.cost,
target.cost_unit = source.cost_unit
WHEN NOT MATCHED THEN
Expand Down Expand Up @@ -89,7 +138,15 @@ func WriteCostMessage(db *sql.DB, message CostMessage) error {
return nil
}

func WriteLcaMessage(db *sql.DB, message LcaMessage) error {
func writeLcaMessageWithRetry(db *sql.DB, message LcaMessage) error {
// pretty print the message
msgJson, err := json.MarshalIndent(message, "", " ")
if err != nil {
log.Printf("Error marshaling message: %v", err)
return fmt.Errorf("failed to marshal message: %w", err)
}
log.Printf("LCA Message:\n%s", string(msgJson))

ctx := context.Background()

tx, err := db.BeginTx(ctx, nil)
Expand All @@ -104,11 +161,11 @@ func WriteLcaMessage(db *sql.DB, message LcaMessage) error {
AS source (project, filename, timestamp, id, category, material_kbob,
gwp_absolute, gwp_relative, penr_absolute, penr_relative,
ubp_absolute, ubp_relative)
ON target.id = source.id
ON target.id = source.id
AND target.project = source.project
AND target.filename = source.filename
WHEN MATCHED THEN
UPDATE SET
target.project = source.project,
target.filename = source.filename,
target.timestamp = source.timestamp,
target.category = source.category,
target.material_kbob = source.material_kbob,
Expand Down

0 comments on commit 4f56e1e

Please sign in to comment.