forked from jason/cpolis
		
	Implement retry logic for UpdateAttributes
This commit is contained in:
		@@ -20,8 +20,7 @@ func (db *DB) AddArticle(a *Article) (int64, error) {
 | 
			
		||||
	query := `
 | 
			
		||||
    INSERT INTO articles
 | 
			
		||||
        (title, description, content, published, rejected, author_id)
 | 
			
		||||
    VALUES
 | 
			
		||||
        (?, ?, ?, ?, ?, ?)
 | 
			
		||||
    VALUES (?, ?, ?, ?, ?, ?)
 | 
			
		||||
    `
 | 
			
		||||
 | 
			
		||||
	result, err := db.Exec(query, a.Title, a.Description, a.Content, a.Published,
 | 
			
		||||
 
 | 
			
		||||
@@ -5,14 +5,19 @@ import (
 | 
			
		||||
	"database/sql"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"math"
 | 
			
		||||
	"math/rand/v2"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-sql-driver/mysql"
 | 
			
		||||
	"golang.org/x/term"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var TxMaxRetries = 3
 | 
			
		||||
 | 
			
		||||
type DB struct {
 | 
			
		||||
	*sql.DB
 | 
			
		||||
}
 | 
			
		||||
@@ -92,29 +97,42 @@ func OpenDB(dbName string) (*DB, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *DB) UpdateAttributes(a ...*Attribute) error {
 | 
			
		||||
	tx, err := db.Begin()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error starting transaction: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, attribute := range a {
 | 
			
		||||
		query := fmt.Sprintf(`
 | 
			
		||||
    UPDATE %s
 | 
			
		||||
    SET %s = ?
 | 
			
		||||
    WHERE id = ?
 | 
			
		||||
    `, attribute.Table, attribute.AttName)
 | 
			
		||||
		if _, err := tx.Exec(query, attribute.Value, attribute.ID); err != nil {
 | 
			
		||||
			if rollbackErr := tx.Rollback(); rollbackErr != nil {
 | 
			
		||||
				log.Fatalf("error: transaction error: %v, rollback error: %v", err, rollbackErr)
 | 
			
		||||
	for i := 0; i < TxMaxRetries; i++ {
 | 
			
		||||
		err := func() error {
 | 
			
		||||
			tx, err := db.Begin()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("error starting transaction: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			return fmt.Errorf("error updating %v in DB: %v", attribute.AttName, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = tx.Commit(); err != nil {
 | 
			
		||||
		return fmt.Errorf("error committing transaction: %v", err)
 | 
			
		||||
			for _, attribute := range a {
 | 
			
		||||
				query := fmt.Sprintf(`
 | 
			
		||||
                    UPDATE %s
 | 
			
		||||
                    SET %s = ?
 | 
			
		||||
                    WHERE id = ?
 | 
			
		||||
                    `, attribute.Table, attribute.AttName)
 | 
			
		||||
				if _, err := tx.Exec(query, attribute.Value, attribute.ID); err != nil {
 | 
			
		||||
					if rollbackErr := tx.Rollback(); rollbackErr != nil {
 | 
			
		||||
						log.Fatalf("error: transaction error: %v, rollback error: %v", err, rollbackErr)
 | 
			
		||||
					}
 | 
			
		||||
					return fmt.Errorf("error updating %v in DB: %v", attribute.AttName, err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if err = tx.Commit(); err != nil {
 | 
			
		||||
				return fmt.Errorf("error committing transaction: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
		}()
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		log.Println(err)
 | 
			
		||||
 | 
			
		||||
		waitTime := time.Duration(math.Pow(2, float64(i))) * time.Second
 | 
			
		||||
		jitter := time.Duration(rand.IntN(1000)) * time.Millisecond
 | 
			
		||||
		time.Sleep(waitTime + jitter)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (db *DB) CountEntries(table string) (int64, error) {
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,7 @@ func (db *DB) AddUser(user *User, pass string) error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	query := `
 | 
			
		||||
    INSERT INTO users
 | 
			
		||||
        (username, password, first_name, last_name, role)
 | 
			
		||||
    INSERT INTO users (username, password, first_name, last_name, role)
 | 
			
		||||
    VALUES (?, ?, ?, ?, ?)
 | 
			
		||||
    `
 | 
			
		||||
	if _, err = db.Exec(query, user.UserName, string(hashedPass), user.FirstName, user.LastName, user.Role); err != nil {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user