Created
November 14, 2024 21:08
-
-
Save nejdetkadir/ff55f4e4c81592bd06d51a82062bae9e to your computer and use it in GitHub Desktop.
Golang + Postgresql Pessimistic and Optimistic locking
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"github.com/gofiber/fiber/v2" | |
"github.com/gofiber/fiber/v2/log" | |
"gorm.io/driver/postgres" | |
"gorm.io/gorm" | |
"gorm.io/gorm/clause" | |
"gorm.io/gorm/logger" | |
) | |
type ( | |
Product struct { | |
ID int | |
Stock int | |
Version int | |
} | |
) | |
func main() { | |
app := fiber.New() | |
db, err := gorm.Open(postgres.Open("<your connection url>"), &gorm.Config{ | |
Logger: logger.Default.LogMode(logger.Info), | |
}) | |
if err != nil { | |
log.Fatalf("Error connecting to database: %v", err) | |
} | |
err = db.AutoMigrate(&Product{}) | |
if err != nil { | |
log.Fatalf("Error migrating schema: %v", err) | |
} | |
var productCount int64 | |
db.Model(&Product{}).Count(&productCount) | |
if productCount == 0 { | |
result := db.Create(&Product{Stock: 100, Version: 0}) | |
if result.Error != nil { | |
log.Fatalf("Error creating product: %v", result.Error) | |
} | |
log.Info("Product created") | |
} else { | |
var product Product | |
result := db.First(&product) | |
if result.Error != nil { | |
log.Fatalf("Error reading product: %v", result.Error) | |
} | |
product.Stock = 100 | |
product.Version = 0 | |
result = db.Save(&product) | |
if result.Error != nil { | |
log.Fatalf("Error updating product: %v", result.Error) | |
} | |
log.Info("Product updated") | |
} | |
pessimisticGroup := app.Group("/pessimistic") | |
optimisticGroup := app.Group("/optimistic") | |
normalGroup := app.Group("/normal") | |
pessimisticGroup.Post("/purchase", func(c *fiber.Ctx) error { | |
var product Product | |
tx := db.Begin() | |
if err = tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&product).Error; err != nil { | |
tx.Rollback() | |
return c.Status(fiber.StatusInternalServerError).SendString("Error reading product") | |
} | |
if product.Stock > 0 { | |
product.Stock-- | |
if err = tx.Save(&product).Error; err != nil { | |
tx.Rollback() | |
return c.Status(fiber.StatusInternalServerError).SendString("Error updating stock") | |
} | |
tx.Commit() | |
return c.JSON(product) | |
} | |
tx.Rollback() | |
return c.SendStatus(fiber.StatusConflict) | |
}) | |
optimisticGroup.Post("/purchase", func(c *fiber.Ctx) error { | |
var product Product | |
tx := db.Begin() | |
if err = tx.First(&product).Error; err != nil { | |
tx.Rollback() | |
return c.Status(fiber.StatusInternalServerError).SendString("Error reading product") | |
} | |
if product.Stock > 0 { | |
if err = tx.Model(&Product{}). | |
Where("id = ? AND version = ?", product.ID, product.Version). | |
Updates(map[string]interface{}{ | |
"stock": product.Stock - 1, | |
"version": product.Version + 1, | |
}).Error; err != nil { | |
tx.Rollback() | |
return c.Status(fiber.StatusConflict).SendString("Optimistic lock conflict") | |
} | |
tx.Commit() | |
return c.JSON(product) | |
} | |
tx.Rollback() | |
return c.SendStatus(fiber.StatusConflict) | |
}) | |
normalGroup.Post("/purchase", func(c *fiber.Ctx) error { | |
var product Product | |
tx := db.Begin() | |
if err = tx.First(&product).Error; err != nil { | |
tx.Rollback() | |
return c.Status(fiber.StatusInternalServerError).SendString("Error reading product") | |
} | |
if product.Stock > 0 { | |
product.Stock-- | |
if err = tx.Save(&product).Error; err != nil { | |
tx.Rollback() | |
return c.Status(fiber.StatusInternalServerError).SendString("Error updating stock") | |
} | |
tx.Commit() | |
return c.JSON(product) | |
} | |
tx.Rollback() | |
return c.SendStatus(fiber.StatusConflict) | |
}) | |
log.Fatal(app.Listen(":3000")) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment