package database import ( "context" "database/sql" "fmt" ) type Repositories struct { db *DB Files *FileRepository Chunks *ChunkRepository Blobs *BlobRepository FileChunks *FileChunkRepository BlobChunks *BlobChunkRepository ChunkFiles *ChunkFileRepository Snapshots *SnapshotRepository Uploads *UploadRepository } func NewRepositories(db *DB) *Repositories { return &Repositories{ db: db, Files: NewFileRepository(db), Chunks: NewChunkRepository(db), Blobs: NewBlobRepository(db), FileChunks: NewFileChunkRepository(db), BlobChunks: NewBlobChunkRepository(db), ChunkFiles: NewChunkFileRepository(db), Snapshots: NewSnapshotRepository(db), Uploads: NewUploadRepository(db.conn), } } type TxFunc func(ctx context.Context, tx *sql.Tx) error func (r *Repositories) WithTx(ctx context.Context, fn TxFunc) error { // Acquire write lock for the entire transaction LogSQL("WithTx", "Acquiring write lock", "") r.db.LockForWrite() defer func() { LogSQL("WithTx", "Releasing write lock", "") r.db.UnlockWrite() }() LogSQL("WithTx", "Beginning transaction", "") tx, err := r.db.BeginTx(ctx, nil) if err != nil { return fmt.Errorf("beginning transaction: %w", err) } LogSQL("WithTx", "Transaction started", "") defer func() { if p := recover(); p != nil { if rollbackErr := tx.Rollback(); rollbackErr != nil { Fatal("failed to rollback transaction: %v", rollbackErr) } panic(p) } else if err != nil { if rollbackErr := tx.Rollback(); rollbackErr != nil { Fatal("failed to rollback transaction: %v", rollbackErr) } } }() err = fn(ctx, tx) if err != nil { return err } return tx.Commit() } func (r *Repositories) WithReadTx(ctx context.Context, fn TxFunc) error { opts := &sql.TxOptions{ ReadOnly: true, } tx, err := r.db.BeginTx(ctx, opts) if err != nil { return fmt.Errorf("beginning read transaction: %w", err) } defer func() { if p := recover(); p != nil { if rollbackErr := tx.Rollback(); rollbackErr != nil { Fatal("failed to rollback transaction: %v", rollbackErr) } panic(p) } else if err != nil { if rollbackErr := tx.Rollback(); rollbackErr != nil { Fatal("failed to rollback transaction: %v", rollbackErr) } } }() err = fn(ctx, tx) if err != nil { return err } return tx.Commit() }