Skip to content

Commit 3e586c7

Browse files
committed
Refactor migration logic into separate migrate.go file
Moved the table migration logic from the RateLimiter struct in sql.go to a standalone Migrate function in a new migrate.go file. This improves code organization and reusability by allowing migration to be called independently of the RateLimiter instance.
1 parent 1c52337 commit 3e586c7

2 files changed

Lines changed: 59 additions & 43 deletions

File tree

sqlrl/migrate.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package sqlrl
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"math/rand"
7+
"strings"
8+
"time"
9+
10+
"github.com/pkg/errors"
11+
"gorm.io/gorm"
12+
)
13+
14+
// Migrate creates the required table if it doesn't exist.
15+
// This method follows the database design standards and handles concurrent migration attempts.
16+
// It's safe to call this method multiple times - it will only create the table if it doesn't exist.
17+
//
18+
// The created table structure follows these principles:
19+
// - Uses VARCHAR(255) for key column to provide format flexibility
20+
// - Uses BIGINT for time storage (unix microseconds)
21+
// - No foreign keys (logical relationships only)
22+
// - Optimized for rate limiting workloads
23+
func Migrate(ctx context.Context, db *gorm.DB, tableName string) error {
24+
// Build CREATE TABLE statement according to database design standards
25+
createTableSQL := fmt.Sprintf(`
26+
CREATE TABLE IF NOT EXISTS %s (
27+
%s VARCHAR(255) PRIMARY KEY NOT NULL,
28+
%s BIGINT NOT NULL
29+
)
30+
`, tableName, ColumnKey, ColumnTimeToAct)
31+
32+
// Retry mechanism to handle concurrent table creation
33+
for attempts := 0; attempts < MaxMigrationAttempts; attempts++ {
34+
err := db.WithContext(ctx).Exec(createTableSQL).Error
35+
if err == nil {
36+
return nil
37+
}
38+
39+
if attempts == MaxMigrationAttempts-1 {
40+
return errors.Wrap(err, "failed to create table")
41+
}
42+
43+
errMsg := err.Error()
44+
// Handle PostgreSQL concurrent table creation conflicts
45+
if strings.Contains(errMsg, `duplicate key value violates unique constraint`) ||
46+
strings.Contains(errMsg, "already exists (SQLSTATE 42P07)") {
47+
select {
48+
case <-ctx.Done():
49+
return errors.Wrap(ctx.Err(), "migration cancelled during backoff")
50+
case <-time.After(time.Duration(100+rand.Intn(100)) * time.Millisecond):
51+
}
52+
continue
53+
}
54+
55+
return errors.Wrap(err, "failed to create table")
56+
}
57+
return nil
58+
}

sqlrl/sql.go

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package sqlrl
33
import (
44
"context"
55
"fmt"
6-
"math/rand"
76
"strings"
87
"time"
98

@@ -83,49 +82,8 @@ func New(db *gorm.DB, tableName string) (*RateLimiter, error) {
8382
}
8483

8584
// Migrate creates the required table if it doesn't exist.
86-
// This method follows the database design standards and handles concurrent migration attempts.
87-
// It's safe to call this method multiple times - it will only create the table if it doesn't exist.
88-
//
89-
// The created table structure follows these principles:
90-
// - Uses VARCHAR(255) for key column to provide format flexibility
91-
// - Uses BIGINT for time storage (unix microseconds)
92-
// - No foreign keys (logical relationships only)
93-
// - Optimized for rate limiting workloads
9485
func (s *RateLimiter) Migrate(ctx context.Context) error {
95-
// Build CREATE TABLE statement according to database design standards
96-
createTableSQL := fmt.Sprintf(`
97-
CREATE TABLE IF NOT EXISTS %s (
98-
%s VARCHAR(255) PRIMARY KEY NOT NULL,
99-
%s BIGINT NOT NULL
100-
)
101-
`, s.tableName, ColumnKey, ColumnTimeToAct)
102-
103-
// Retry mechanism to handle concurrent table creation
104-
for attempts := 0; attempts < MaxMigrationAttempts; attempts++ {
105-
err := s.db.WithContext(ctx).Exec(createTableSQL).Error
106-
if err == nil {
107-
return nil
108-
}
109-
110-
if attempts == MaxMigrationAttempts-1 {
111-
return errors.Wrap(err, "failed to create table")
112-
}
113-
114-
errMsg := err.Error()
115-
// Handle PostgreSQL concurrent table creation conflicts
116-
if strings.Contains(errMsg, `duplicate key value violates unique constraint`) ||
117-
strings.Contains(errMsg, "already exists (SQLSTATE 42P07)") {
118-
select {
119-
case <-ctx.Done():
120-
return errors.Wrap(ctx.Err(), "migration cancelled during backoff")
121-
case <-time.After(time.Duration(100+rand.Intn(100)) * time.Millisecond):
122-
}
123-
continue
124-
}
125-
126-
return errors.Wrap(err, "failed to create table")
127-
}
128-
return nil
86+
return Migrate(ctx, s.db, s.tableName)
12987
}
13088

13189
type kvWrapper struct {

0 commit comments

Comments
 (0)