|
| 1 | +package main |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "fmt" |
| 6 | + "testing" |
| 7 | + |
| 8 | + "github.com/jackc/pgx/v5" |
| 9 | + "github.com/stretchr/testify/assert" |
| 10 | + "github.com/stretchr/testify/require" |
| 11 | +) |
| 12 | + |
| 13 | +func countOnShardByComment(t *testing.T, conn *pgx.Conn, shard int, id int64) int { |
| 14 | + t.Helper() |
| 15 | + |
| 16 | + var count int |
| 17 | + err := conn.QueryRow(context.Background(), |
| 18 | + fmt.Sprintf("/* pgdog_shard: %d */ SELECT COUNT(*) FROM sharded WHERE id = $1", shard), id).Scan(&count) |
| 19 | + require.NoError(t, err) |
| 20 | + return count |
| 21 | +} |
| 22 | + |
| 23 | +func TestBatchRoutesShardedTable(t *testing.T) { |
| 24 | + ctx := context.Background() |
| 25 | + |
| 26 | + proxy, err := pgx.Connect(ctx, testConnStr) |
| 27 | + require.NoError(t, err) |
| 28 | + defer proxy.Close(ctx) |
| 29 | + |
| 30 | + _, err = proxy.Exec(ctx, "TRUNCATE TABLE sharded") |
| 31 | + require.NoError(t, err) |
| 32 | + |
| 33 | + const shard0ID int64 = 1 |
| 34 | + const shard1ID int64 = 11 |
| 35 | + |
| 36 | + batch := &pgx.Batch{} |
| 37 | + batch.Queue("INSERT INTO sharded (id, value) VALUES ($1, $2)", shard0ID, "shard_0") |
| 38 | + batch.Queue("INSERT INTO sharded (id, value) VALUES ($1, $2)", shard1ID, "shard_1") |
| 39 | + |
| 40 | + results := proxy.SendBatch(ctx, batch) |
| 41 | + for i := range 2 { |
| 42 | + tag, err := results.Exec() |
| 43 | + require.NoError(t, err, "batch insert %d", i) |
| 44 | + assert.EqualValues(t, 1, tag.RowsAffected(), "batch insert %d rows affected", i) |
| 45 | + } |
| 46 | + require.NoError(t, results.Close()) |
| 47 | + |
| 48 | + assert.Equal(t, 1, countOnShardByComment(t, proxy, 0, shard0ID), "id=%d must be on shard 0", shard0ID) |
| 49 | + assert.Equal(t, 0, countOnShardByComment(t, proxy, 1, shard0ID), "id=%d must not be on shard 1", shard0ID) |
| 50 | + assert.Equal(t, 0, countOnShardByComment(t, proxy, 0, shard1ID), "id=%d must not be on shard 0", shard1ID) |
| 51 | + assert.Equal(t, 1, countOnShardByComment(t, proxy, 1, shard1ID), "id=%d must be on shard 1", shard1ID) |
| 52 | +} |
| 53 | + |
| 54 | +func TestBatchRollsBackOnError(t *testing.T) { |
| 55 | + ctx := context.Background() |
| 56 | + |
| 57 | + proxy, err := pgx.Connect(ctx, testConnStr) |
| 58 | + require.NoError(t, err) |
| 59 | + defer proxy.Close(ctx) |
| 60 | + |
| 61 | + _, err = proxy.Exec(ctx, "TRUNCATE TABLE sharded") |
| 62 | + require.NoError(t, err) |
| 63 | + |
| 64 | + const shard0ID int64 = 1 |
| 65 | + const shard1ID int64 = 11 |
| 66 | + |
| 67 | + batch := &pgx.Batch{} |
| 68 | + batch.Queue("INSERT INTO sharded (id, value) VALUES ($1, $2)", shard0ID, "shard_0") |
| 69 | + batch.Queue("INSERT INTO sharded (id, value) VALUES ($1, $2)", shard1ID, "shard_1") |
| 70 | + batch.Queue("INSERT INTO sharded (id, value) VALUES ($1, $2)", shard0ID, "shard_0") |
| 71 | + |
| 72 | + results := proxy.SendBatch(ctx, batch) |
| 73 | + tag, err := results.Exec() |
| 74 | + require.NoError(t, err, "batch insert 1") |
| 75 | + assert.EqualValues(t, 1, tag.RowsAffected(), "batch insert 1 rows affected") |
| 76 | + tag, err = results.Exec() |
| 77 | + require.NoError(t, err, "batch insert 2") |
| 78 | + assert.EqualValues(t, 1, tag.RowsAffected(), "batch insert 2 rows affected") |
| 79 | + |
| 80 | + _, err = results.Exec() |
| 81 | + require.ErrorContains(t, err, "duplicate key value violates unique constraint") |
| 82 | + require.Error(t, results.Close()) |
| 83 | + |
| 84 | + assert.Equal(t, 0, countOnShardByComment(t, proxy, 0, shard0ID), "transaction must have rolled back") |
| 85 | + assert.Equal(t, 0, countOnShardByComment(t, proxy, 1, shard0ID), "transaction must have rolled back") |
| 86 | +} |
0 commit comments