78 lines
2.2 KiB
Go
78 lines
2.2 KiB
Go
package dbsync
|
||
|
||
import (
|
||
"context"
|
||
"database/sql"
|
||
"fmt"
|
||
"time"
|
||
)
|
||
|
||
const FlagTable = "_ajz_sync_flag"
|
||
|
||
// EnsureFlagTable:SQLite 用表标记「正在应用远端变更」,避免触发器回声。
|
||
func EnsureFlagTable(ctx context.Context, db *sql.DB, driver Driver) error {
|
||
if driver != DriverSQLite {
|
||
return nil
|
||
}
|
||
_, err := db.ExecContext(ctx, `CREATE TABLE IF NOT EXISTS `+FlagTable+` (
|
||
k TEXT PRIMARY KEY,
|
||
v INTEGER NOT NULL DEFAULT 0
|
||
);`)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
_, _ = db.ExecContext(ctx, `INSERT OR IGNORE INTO `+FlagTable+`(k,v) VALUES('applying',0)`)
|
||
return nil
|
||
}
|
||
|
||
// WithApplying 在写入目标库时置位,使触发器不写 outbox(防 A↔B 回声导致「多」)。
|
||
func WithApplying(ctx context.Context, db *sql.DB, driver Driver, fn func() error) error {
|
||
switch driver {
|
||
case DriverSQLite:
|
||
if err := EnsureFlagTable(ctx, db, driver); err != nil {
|
||
return err
|
||
}
|
||
if _, err := db.ExecContext(ctx, `UPDATE `+FlagTable+` SET v=1 WHERE k='applying'`); err != nil {
|
||
return err
|
||
}
|
||
defer func() {
|
||
_, _ = db.ExecContext(context.Background(), `UPDATE `+FlagTable+` SET v=0 WHERE k='applying'`)
|
||
}()
|
||
return fn()
|
||
case DriverMySQL:
|
||
if _, err := db.ExecContext(ctx, `SET @ajz_applying = 1`); err != nil {
|
||
return err
|
||
}
|
||
defer func() { _, _ = db.ExecContext(context.Background(), `SET @ajz_applying = 0`) }()
|
||
return fn()
|
||
case DriverPostgres:
|
||
if _, err := db.ExecContext(ctx, `SELECT set_config('ajz.applying', '1', false)`); err != nil {
|
||
return err
|
||
}
|
||
defer func() {
|
||
_, _ = db.ExecContext(context.Background(), `SELECT set_config('ajz.applying', '0', false)`)
|
||
}()
|
||
return fn()
|
||
default:
|
||
return fn()
|
||
}
|
||
}
|
||
|
||
func EnqueueOutbox(ctx context.Context, db *sql.DB, driver Driver, table, pk, op, payload string, version int64) error {
|
||
if version <= 0 {
|
||
version = time.Now().UnixMilli()
|
||
}
|
||
switch driver {
|
||
case DriverPostgres:
|
||
_, err := db.ExecContext(ctx, fmt.Sprintf(`
|
||
INSERT INTO %s(table_name,row_pk,op,payload,version) VALUES($1,$2,$3,$4,$5)`, OutboxTable),
|
||
table, pk, op, payload, version)
|
||
return err
|
||
default:
|
||
_, err := db.ExecContext(ctx, fmt.Sprintf(`
|
||
INSERT INTO %s(table_name,row_pk,op,payload,version) VALUES(?,?,?,?,?)`, OutboxTable),
|
||
table, pk, op, payload, version)
|
||
return err
|
||
}
|
||
}
|