Skip to content

Commit

Permalink
feat: upsertcounter as a general method
Browse files Browse the repository at this point in the history
  • Loading branch information
mgierada committed Jun 3, 2024
1 parent 59ebcc5 commit 83bc185
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 67 deletions.
2 changes: 1 addition & 1 deletion server/coroutines/counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func runBackgroundTask(ctx context.Context) {
for {
select {
case <-ticker.C:
err := db.UpsertCounterData()
err := db.UpdateCounter()
if err != nil {
log.Println(err)
}
Expand Down
18 changes: 9 additions & 9 deletions server/db/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,14 @@ func TestGetCounterEmpty(t *testing.T) {
// NOTE: Test covers a situation where there are no existing rows in the counter table and we
// simply need to create one with CurrentValue=1 and UpdatedAt=NOW(). ResetedAt should be a sql
// null string
func TestUpsertCounterData(t *testing.T) {
func TestUpdateCounter(t *testing.T) {
// Ensure db_test is not nil
if db == nil {
t.Fatal("db is nil")
}

// Call the UpsertCounterData function
err := UpsertCounterData()
// Call the UpdateCounter function
err := UpdateCounter()
if err != nil {
t.Fatalf("failed to upsert counter data: %s", err)
}
Expand Down Expand Up @@ -214,7 +214,7 @@ func TestUpsertCounterData(t *testing.T) {
// NOTE: Test covers a typical situation where there are some existing rows in the counter table
// and we simply need to update the counter. It is expected to update a increment the counter by
// one and update the updated_at field to NOW().
func TestUpsertCounterDataTypicalCase(t *testing.T) {
func TestUpdateCounterTypicalCase(t *testing.T) {
// Insert a row into the counter table
_, ierr := db.Exec(`INSERT INTO counter (current_value, updated_at) VALUES (42, '2024-05-28 12:34:56')`)
if ierr != nil {
Expand All @@ -226,8 +226,8 @@ func TestUpsertCounterDataTypicalCase(t *testing.T) {
t.Fatal("db is nil")
}

// Test UpsertCounterData
err := UpsertCounterData()
// Test UpdateCounter
err := UpdateCounter()
if err != nil {
t.Fatalf("failed to upsert counter data: %s", err)
}
Expand Down Expand Up @@ -266,7 +266,7 @@ func TestUpsertCounterDataTypicalCase(t *testing.T) {
// NOTE: Test covers a situation where there are some existing rows in the counter table and we
// cannot update the counter because 24h did not pass since the last update.
// It is expected that no counter is updated.
func TestUpsertCounterDataTimeDidNotPass(t *testing.T) {
func TestUpdateCounterTimeDidNotPass(t *testing.T) {
// Check updated_at (should be close to current time)
updatedLessThan24hAgo := time.Now().UTC().Add(-23 * time.Hour)
parsedUpdatedLessThan24hAgo := updatedLessThan24hAgo.Format(time.RFC3339)
Expand All @@ -282,8 +282,8 @@ func TestUpsertCounterDataTimeDidNotPass(t *testing.T) {
t.Fatal("db is nil")
}

// Test UpsertCounterData
err := UpsertCounterData()
// Test UpdateCounter
err := UpdateCounter()
if err != nil {
t.Fatalf("failed to upsert counter data: %s", err)
}
Expand Down
54 changes: 0 additions & 54 deletions server/db/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,57 +49,3 @@ func Connect() {
models.CreateHistoricalCountersTableIfNotExists(db)
models.CreateOhnoCounterTableIfNotExists(db)
}

func createHistoricalCountersTableIfNotExists() {
// Create the historical_counters table
createTableQuery := `
CREATE TABLE IF NOT EXISTS historical_counters (
counter_id UUID PRIMARY KEY NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
value INT NOT NULL
);
`
_, err := db.Exec(createTableQuery)
if err != nil {
log.Fatalf("❌ Error creating historical_counters table.\n %s", err)
}

// Create or replace the trigger function
createTriggerFunctionQuery := `
CREATE OR REPLACE FUNCTION update_historical_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
`
_, err = db.Exec(createTriggerFunctionQuery)
if err != nil {
log.Fatalf("❌ Error creating trigger function for historical_counters table.\n %s", err)
}

// Create the trigger conditionally
createTriggerQuery := `
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_trigger
WHERE tgname = 'update_historical_updated_at'
) THEN
CREATE TRIGGER update_historical_updated_at
BEFORE UPDATE ON historical_counters
FOR EACH ROW
EXECUTE FUNCTION update_historical_updated_at_column();
END IF;
END $$;
`
_, err = db.Exec(createTriggerQuery)
if err != nil {
log.Fatalf("❌ Error creating trigger for historical_counters table.\n %s", err)
}

log.Println("✅ Ensured historical_counters table and trigger exist.")
}
26 changes: 24 additions & 2 deletions server/db/update_counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,29 @@ type Counter struct {
ResetedAt sql.NullString
}

func UpsertCounterData() error {
func upsertCounterData(tableName string) error {

if tableName == "" {
return fmt.Errorf("❌ Error upserting counter data. Table name cannot be empty.")
}

tx, err := db.Begin()
if err != nil {
return fmt.Errorf("❌ Error starting transaction.\n %s", err)
}

var counter Counter

err = tx.QueryRow("SELECT current_value, updated_at, reseted_at FROM counter LIMIT 1 FOR UPDATE").Scan(&counter.CurrentValue, &counter.UpdatedAt, &counter.ResetedAt)
upsertCounterQuery := fmt.Sprintf(`
SELECT
current_value, updated_at, reseted_at
FROM
%s
LIMIT 1
FOR UPDATE
`, tableName)

err = tx.QueryRow(upsertCounterQuery).Scan(&counter.CurrentValue, &counter.UpdatedAt, &counter.ResetedAt)
if err != nil {
if err == sql.ErrNoRows {
log.Println("No rows found in counter table. Inserting new row.")
Expand Down Expand Up @@ -123,3 +137,11 @@ func SetCounter(value int) error {
log.Println("✅ Transaction committed successfully")
return nil
}

func UpdateCounter() error {
return upsertCounterData("counter")
}

func UpdateOhnoCounter() error {
return upsertCounterData("ohno_counter")
}
2 changes: 1 addition & 1 deletion server/handlers/counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func IncrementCounter(w http.ResponseWriter, r *http.Request) {
log.Printf("🔗 received /increment request")
switch r.Method {
case "POST":
db.UpsertCounterData()
db.UpdateCounter()
response := ServerResponse{Message: "Counter incremented successfully"}
MarshalJson(w, http.StatusOK, response)
log.Println("🟢 Counter incremented successfully")
Expand Down

0 comments on commit 83bc185

Please sign in to comment.