Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
require:
plugins:
- rubocop-rake
- rubocop-minitest

Expand Down
7 changes: 6 additions & 1 deletion exe/outboxify
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
require 'sequel/pgt_outbox'
require 'optparse'

autovacuum = true

opts = OptionParser.new do |o|
o.banner = 'Usage: outboxify [options] <db uri> <table>'
o.on('--no-autovacuum') { autovacuum = false }
end
opts.parse!

db_uri = ARGV.shift
if db_uri.nil?
Expand All @@ -25,5 +29,6 @@ if table.nil?
exit 2
end

function = DB.pgt_outbox_setup(table)
options = { autovacuum: }
function = DB.pgt_outbox_setup(table, **options)
DB.pgt_outbox_events(table, function)
1 change: 1 addition & 0 deletions lib/sequel/pgt_outbox/function.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module PgtOutbox
# The Outbox Function
class Function
include PgtOutbox

DEFAULT_OPTS = { language: :plpgsql, returns: :trigger, replace: true }.freeze

attr_reader(*%i[outbox db opts])
Expand Down
39 changes: 39 additions & 0 deletions lib/sequel/pgt_outbox/table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def create!
string_columns!
jsonb_columns!
indexes!
autovacuum_settings!
self
end

Expand Down Expand Up @@ -99,6 +100,30 @@ def uuid_function
@uuid_function ||= opts.fetch(:uuid_function, :uuid_generate_v4)
end

def autovacuum?
@autovacuum ||= opts.fetch(:autovacuum, true)
end

def autovacuum_vacuum_scale_factor
opts.fetch(:autovacuum_vacuum_scale_factor, 0)
end

def autovacuum_vacuum_threshold
opts.fetch(:autovacuum_vacuum_threshold, 50)
end

def autovacuum_analyze_scale_factor
opts.fetch(:autovacuum_analyze_scale_factor, 0)
end

def autovacuum_analyze_threshold
opts.fetch(:autovacuum_analyze_threshold, 50)
end

def autovacuum_vacuum_cost_delay
opts.fetch(:autovacuum_vacuum_cost_delay, 0)
end

def function
@function ||= Function.create!(self, opts:)
end
Expand Down Expand Up @@ -156,6 +181,20 @@ def indexes!
db.add_index name, Sequel.desc(attempted_column)
self
end

def autovacuum_settings!
return unless autovacuum?

settings = {
autovacuum_vacuum_scale_factor: autovacuum_vacuum_scale_factor,
autovacuum_vacuum_threshold: autovacuum_vacuum_threshold,
autovacuum_analyze_scale_factor: autovacuum_analyze_scale_factor,
autovacuum_analyze_threshold: autovacuum_analyze_threshold,
autovacuum_vacuum_cost_delay: autovacuum_vacuum_cost_delay
}
db.run "ALTER TABLE #{quoted_name} SET (#{settings.map { |k, v| "#{k} = #{v}" }.join(", ")})"
self
end
end
end
end
87 changes: 87 additions & 0 deletions test/sequel/test_pgt_outbox.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,10 @@ def depth_sql(depth)

ds = DB[:accounts]
ds.insert(id: 1, s: 'string')

_(ds.all).must_equal [{ id: 1, s: 'string' }]
h = @logs.first

_(h.delete(:created).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
_(h.delete(:updated).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
_(h).must_equal(id: 1,
Expand All @@ -75,8 +77,10 @@ def depth_sql(depth)
metadata: nil)

ds.where(id: 1).update(s: 'string2')

_(ds.all).must_equal [{ id: 1, s: 'string2' }]
h = @logs.first

_(h.delete(:created).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
_(h.delete(:updated).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
_(h).must_equal(id: 2,
Expand All @@ -90,8 +94,10 @@ def depth_sql(depth)
metadata: nil)

ds.delete

_(ds.all).must_equal []
h = @logs.first

_(h.delete(:created).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
_(h.delete(:updated).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
_(h).must_equal(id: 3,
Expand Down Expand Up @@ -130,21 +136,27 @@ def depth_sql(depth)

ds = DB[:accounts]
ds.insert(id: 1, s: 'string')

_(ds.all).must_equal [{ id: 1, s: 'string' }]
h = @logs.first

_(h.delete(:created).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
_(h.delete(:updated).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
id = h.delete(:id)

_(id).must_match(/\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/)
_(h).must_equal(attempts: 0, attempted: nil, completed: nil, event_type: 'accounts_created', last_error: nil,
data_before: nil, data_after: { 's' => 'string', 'id' => 1 }, metadata: nil)

ds.where(id: 1).update(s: 'string2')

_(ds.all).must_equal [{ id: 1, s: 'string2' }]
h = @logs.first

_(h.delete(:created).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
_(h.delete(:updated).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
id = h.delete(:id)

_(id).must_match(/\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/)
_(h).must_equal(attempts: 0,
attempted: nil,
Expand All @@ -156,11 +168,14 @@ def depth_sql(depth)
metadata: nil)

ds.delete

_(ds.all).must_equal []
h = @logs.first

_(h.delete(:created).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
_(h.delete(:updated).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
id = h.delete(:id)

_(id).must_match(/\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/)
_(h).must_equal(attempts: 0, attempted: nil, completed: nil, event_type: 'accounts_deleted', last_error: nil,
data_before: { 's' => 'string2', 'id' => 1 }, data_after: nil, metadata: nil)
Expand Down Expand Up @@ -192,21 +207,27 @@ def depth_sql(depth)

ds = DB[:accounts]
ds.insert(id: 1, s: 'string')

_(ds.all).must_equal [{ id: 1, s: 'string' }]
h = @logs.first

_(h.delete(:created).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
_(h.delete(:updated).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
id = h.delete(:id)

_(id).must_match(/\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/)
_(h).must_equal(attempts: 0, attempted: nil, completed: false, event_type: 'accounts_created', last_error: nil,
data_before: nil, data_after: { 's' => 'string', 'id' => 1 }, metadata: nil)

ds.where(id: 1).update(s: 'string2')

_(ds.all).must_equal [{ id: 1, s: 'string2' }]
h = @logs.first

_(h.delete(:created).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
_(h.delete(:updated).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
id = h.delete(:id)

_(id).must_match(/\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/)
_(h).must_equal(attempts: 0,
attempted: nil,
Expand All @@ -218,16 +239,82 @@ def depth_sql(depth)
metadata: nil)

ds.delete

_(ds.all).must_equal []
h = @logs.first

_(h.delete(:created).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
_(h.delete(:updated).to_i).must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
id = h.delete(:id)

_(id).must_match(/\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/)
_(h).must_equal(attempts: 0, attempted: nil, completed: false, event_type: 'accounts_deleted', last_error: nil,
data_before: { 's' => 'string2', 'id' => 1 }, data_after: nil, metadata: nil)
end
end
end

if DB.server_version >= 90_400
describe 'Autovacuum Settings' do # rubocop:disable Metrics/BlockLength
def get_reloptions(table_name)
DB['SELECT reloptions FROM pg_class WHERE relname = ?', table_name].first[:reloptions]
end

after do
DB.drop_table(:accounts, :accounts_outbox)
begin
DB.drop_function(:spgt_outbox_events)
rescue Sequel::DatabaseError
# function may not exist
end
end

it 'should apply default autovacuum settings by default' do
DB.create_table!(:accounts) do
integer :id
String :s
end
DB.pgt_outbox_setup(:accounts, function_name: :spgt_outbox_events)

opts = get_reloptions('accounts_outbox')

_(opts).must_include 'autovacuum_vacuum_scale_factor=0'
_(opts).must_include 'autovacuum_vacuum_threshold=50'
_(opts).must_include 'autovacuum_analyze_scale_factor=0'
_(opts).must_include 'autovacuum_analyze_threshold=50'
_(opts).must_include 'autovacuum_vacuum_cost_delay=0'
end

it 'should skip autovacuum settings when autovacuum: false' do
DB.create_table!(:accounts) do
integer :id
String :s
end
DB.pgt_outbox_setup(:accounts, autovacuum: false, function_name: :spgt_outbox_events)

opts = get_reloptions('accounts_outbox')

_(opts).must_be_nil
end

it 'should apply custom autovacuum thresholds' do
DB.create_table!(:accounts) do
integer :id
String :s
end
DB.pgt_outbox_setup(:accounts,
function_name: :spgt_outbox_events,
autovacuum_vacuum_threshold: 100,
autovacuum_analyze_threshold: 200,
autovacuum_vacuum_cost_delay: 20)

opts = get_reloptions('accounts_outbox')

_(opts).must_include 'autovacuum_vacuum_threshold=100'
_(opts).must_include 'autovacuum_analyze_threshold=200'
_(opts).must_include 'autovacuum_vacuum_cost_delay=20'
end
end
end

# vim: ft=ruby sts=2 sw=2 ts=2 et
Loading