mirror of
https://github.com/postgresml/pgcat.git
synced 2026-03-27 10:46:30 +00:00
@@ -10,6 +10,8 @@ jobs:
|
|||||||
# See: https://circleci.com/docs/2.0/configuration-reference/#docker-machine-macos-windows-executor
|
# See: https://circleci.com/docs/2.0/configuration-reference/#docker-machine-macos-windows-executor
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/rust:1.58.1
|
- image: cimg/rust:1.58.1
|
||||||
|
environment:
|
||||||
|
RUST_LOG: info
|
||||||
- image: cimg/postgres:14.0
|
- image: cimg/postgres:14.0
|
||||||
auth:
|
auth:
|
||||||
username: mydockerhub-user
|
username: mydockerhub-user
|
||||||
|
|||||||
@@ -1,59 +1,27 @@
|
|||||||
require "active_record"
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'active_record'
|
||||||
|
|
||||||
# Uncomment these two to see all queries.
|
# Uncomment these two to see all queries.
|
||||||
# ActiveRecord.verbose_query_logs = true
|
# ActiveRecord.verbose_query_logs = true
|
||||||
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
||||||
|
|
||||||
ActiveRecord::Base.establish_connection(
|
ActiveRecord::Base.establish_connection(
|
||||||
adapter: "postgresql",
|
adapter: 'postgresql',
|
||||||
host: "127.0.0.1",
|
host: '127.0.0.1',
|
||||||
port: 6432,
|
port: 6432,
|
||||||
username: "sharding_user",
|
username: 'sharding_user',
|
||||||
password: "sharding_user",
|
password: 'sharding_user',
|
||||||
database: "rails_dev",
|
database: 'rails_dev',
|
||||||
prepared_statements: false, # Transaction mode
|
prepared_statements: false, # Transaction mode
|
||||||
advisory_locks: false, # Same
|
advisory_locks: false # Same
|
||||||
)
|
)
|
||||||
|
|
||||||
class TestTable < ActiveRecord::Base
|
|
||||||
self.table_name = "test_table"
|
|
||||||
end
|
|
||||||
|
|
||||||
class TestSafeTable < ActiveRecord::Base
|
class TestSafeTable < ActiveRecord::Base
|
||||||
self.table_name = "test_safe_table"
|
self.table_name = 'test_safe_table'
|
||||||
end
|
end
|
||||||
|
|
||||||
class ShouldNeverHappenException < Exception
|
class ShouldNeverHappenException < RuntimeError
|
||||||
end
|
|
||||||
|
|
||||||
# # Create the table.
|
|
||||||
class CreateTestTable < ActiveRecord::Migration[7.0]
|
|
||||||
# Disable transasctions or things will fly out of order!
|
|
||||||
disable_ddl_transaction!
|
|
||||||
|
|
||||||
SHARDS = 3
|
|
||||||
|
|
||||||
def change
|
|
||||||
SHARDS.times do |x|
|
|
||||||
# This will make this migration reversible!
|
|
||||||
reversible do
|
|
||||||
connection.execute "SET SHARD TO '#{x.to_i}'"
|
|
||||||
connection.execute "SET SERVER ROLE TO 'primary'"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Always wrap the entire migration inside a transaction. If that's not possible,
|
|
||||||
# execute a `SET SHARD` command before every statement and make sure AR doesn't need
|
|
||||||
# to load database information beforehand (i.e. it's not the first query in the migration).
|
|
||||||
connection.transaction do
|
|
||||||
create_table :test_table, if_not_exists: true do |t|
|
|
||||||
t.string :name
|
|
||||||
t.string :description
|
|
||||||
|
|
||||||
t.timestamps
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class CreateSafeShardedTable < ActiveRecord::Migration[7.0]
|
class CreateSafeShardedTable < ActiveRecord::Migration[7.0]
|
||||||
@@ -85,44 +53,51 @@ class CreateSafeShardedTable < ActiveRecord::Migration[7.0]
|
|||||||
SHARDS.times do |x|
|
SHARDS.times do |x|
|
||||||
connection.execute "SET SHARD TO '#{x.to_i}'"
|
connection.execute "SET SHARD TO '#{x.to_i}'"
|
||||||
connection.execute "SET SERVER ROLE TO 'primary'"
|
connection.execute "SET SERVER ROLE TO 'primary'"
|
||||||
connection.execute "DROP TABLE test_safe_table CASCADE"
|
connection.execute 'DROP TABLE test_safe_table CASCADE'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
20.times do
|
SHARDS = 3
|
||||||
begin
|
|
||||||
CreateTestTable.migrate(:down)
|
|
||||||
rescue Exception
|
|
||||||
puts "Tables don't exist yet"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
2.times do
|
||||||
begin
|
begin
|
||||||
CreateSafeShardedTable.migrate(:down)
|
CreateSafeShardedTable.migrate(:down)
|
||||||
rescue Exception
|
rescue Exception
|
||||||
puts "Tables don't exist yet"
|
puts "Tables don't exist yet"
|
||||||
end
|
end
|
||||||
|
|
||||||
CreateTestTable.migrate(:up)
|
|
||||||
CreateSafeShardedTable.migrate(:up)
|
CreateSafeShardedTable.migrate(:up)
|
||||||
|
|
||||||
3.times do |x|
|
SHARDS.times do |x|
|
||||||
TestSafeTable.connection.execute "SET SHARD TO '#{x.to_i}'"
|
TestSafeTable.connection.execute "SET SHARD TO '#{x.to_i}'"
|
||||||
TestSafeTable.connection.execute "SET SERVER ROLE TO 'primary'"
|
TestSafeTable.connection.execute "SET SERVER ROLE TO 'primary'"
|
||||||
TestSafeTable.connection.execute "TRUNCATE #{TestTable.table_name}"
|
TestSafeTable.connection.execute "TRUNCATE #{TestSafeTable.table_name}"
|
||||||
end
|
end
|
||||||
|
|
||||||
10.times do |x|
|
# Equivalent to Makara's stick_to_master! except it sticks until it's changed.
|
||||||
|
TestSafeTable.connection.execute "SET SERVER ROLE TO 'primary'"
|
||||||
|
|
||||||
|
200.times do |x|
|
||||||
x += 1 # Postgres ids start at 1
|
x += 1 # Postgres ids start at 1
|
||||||
TestSafeTable.connection.execute "SET SHARDING KEY TO '#{x.to_i}'"
|
TestSafeTable.connection.execute "SET SHARDING KEY TO '#{x.to_i}'"
|
||||||
TestSafeTable.connection.execute "SET SERVER ROLE TO 'primary'"
|
|
||||||
TestSafeTable.create(id: x, name: "something_special_#{x.to_i}", description: "It's a surprise!")
|
TestSafeTable.create(id: x, name: "something_special_#{x.to_i}", description: "It's a surprise!")
|
||||||
end
|
end
|
||||||
|
|
||||||
10.times do |x|
|
TestSafeTable.connection.execute "SET SERVER ROLE TO 'replica'"
|
||||||
|
|
||||||
|
100.times do |x|
|
||||||
x += 1 # 0 confuses our sharding function
|
x += 1 # 0 confuses our sharding function
|
||||||
TestSafeTable.connection.execute "SET SHARDING KEY TO '#{x.to_i}'"
|
TestSafeTable.connection.execute "SET SHARDING KEY TO '#{x.to_i}'"
|
||||||
TestSafeTable.connection.execute "SET SERVER ROLE TO 'replica'"
|
TestSafeTable.find_by_id(x).id
|
||||||
|
end
|
||||||
|
|
||||||
|
# Will use the query parser to direct reads to replicas
|
||||||
|
TestSafeTable.connection.execute "SET SERVER ROLE TO 'auto'"
|
||||||
|
|
||||||
|
100.times do |x|
|
||||||
|
x += 101
|
||||||
|
TestSafeTable.connection.execute "SET SHARDING KEY TO '#{x.to_i}'"
|
||||||
TestSafeTable.find_by_id(x).id
|
TestSafeTable.find_by_id(x).id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -130,8 +105,8 @@ end
|
|||||||
# Test wrong shard
|
# Test wrong shard
|
||||||
TestSafeTable.connection.execute "SET SHARD TO '1'"
|
TestSafeTable.connection.execute "SET SHARD TO '1'"
|
||||||
begin
|
begin
|
||||||
TestSafeTable.create(id: 5, name: "test", description: "test description")
|
TestSafeTable.create(id: 5, name: 'test', description: 'test description')
|
||||||
raise ShouldNeverHappenException("Uh oh")
|
raise ShouldNeverHappenException('Uh oh')
|
||||||
rescue ActiveRecord::StatementInvalid
|
rescue ActiveRecord::StatementInvalid
|
||||||
puts "OK"
|
puts 'OK'
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user