package storage import ( "context" "database/sql" "fmt" "github.com/go-mysql-org/go-mysql/mysql" "dashboard-cdc-worker/internal/config" ) const serviceName = "dashboard-cdc-worker" type MySQLRepository struct { db *sql.DB cfg config.Config } func NewMySQLRepository(db *sql.DB, cfg config.Config) *MySQLRepository { return &MySQLRepository{db: db, cfg: cfg} } func (r *MySQLRepository) WithTx(ctx context.Context, fn func(*sql.Tx) error) error { tx, err := r.db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadCommitted}) if err != nil { return err } if err := fn(tx); err != nil { _ = tx.Rollback() return err } return tx.Commit() } func (r *MySQLRepository) LoadCheckpoint(ctx context.Context) (mysql.Position, bool, error) { var file string var pos uint32 err := r.db.QueryRowContext(ctx, ` SELECT binlog_file, binlog_pos FROM dashboard_cdc_checkpoint WHERE service_name = ? `, serviceName).Scan(&file, &pos) if err == sql.ErrNoRows { return mysql.Position{}, false, nil } if err != nil { return mysql.Position{}, false, err } return mysql.Position{Name: file, Pos: pos}, true, nil } func (r *MySQLRepository) SaveCheckpoint(ctx context.Context, pos mysql.Position) error { _, err := r.db.ExecContext(ctx, ` INSERT INTO dashboard_cdc_checkpoint (service_name, binlog_file, binlog_pos, updated_at) VALUES (?, ?, ?, NOW()) ON DUPLICATE KEY UPDATE binlog_file = VALUES(binlog_file), binlog_pos = VALUES(binlog_pos), updated_at = NOW() `, serviceName, pos.Name, pos.Pos) return err } func (r *MySQLRepository) MarkEventApplied(ctx context.Context, tx *sql.Tx, eventKey string, source string) (bool, error) { result, err := tx.ExecContext(ctx, ` INSERT IGNORE INTO dashboard_cdc_event_dedup (event_key, source, created_at) VALUES (?, ?, NOW()) `, eventKey, source) if err != nil { return false, err } affected, err := result.RowsAffected() if err != nil { return false, err } return affected > 0, nil } func execAll(ctx context.Context, db *sql.DB, statements []string) error { for _, stmt := range statements { if _, err := db.ExecContext(ctx, stmt); err != nil { return fmt.Errorf("exec schema failed: %w; sql=%s", err, stmt) } } return nil }