Skip to content

Commit

Permalink
[#5] fix parsing duration add specs
Browse files Browse the repository at this point in the history
  • Loading branch information
pawurb committed Oct 2, 2024
1 parent 8d26e9a commit 1e032f8
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 37 deletions.
42 changes: 26 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,15 @@ jobs:
docker run --env POSTGRES_USER=postgres \
--env POSTGRES_DB=pg-locks-monitor-test \
--env POSTGRES_PASSWORD=secret \
-d -p 5436:5432 postgres:15.1-alpine \
-d -p 5436:5432 postgres:15.8-alpine \
postgres -c shared_preload_libraries=pg_stat_statements
sleep 15
- name: Run PostgreSQL 16
run: |
docker run --env POSTGRES_USER=postgres \
--env POSTGRES_DB=pg-locks-monitor-test \
--env POSTGRES_PASSWORD=secret \
-d -p 5437:5432 postgres:16.4-alpine \
postgres -c shared_preload_libraries=pg_stat_statements
sleep 15
- name: Set up Ruby ${{ matrix.ruby-version }}
Expand All @@ -73,17 +81,7 @@ jobs:
POSTGRES_PASSWORD: secret
DATABASE_URL: postgresql://postgres:secret@localhost:5432/pg-locks-monitor-test
run: |
bundle exec rspec spec/
- name: Run tests for PG 11
env:
PG_VERSION: 11
POSTGRES_HOST: localhost
POSTGRES_USER: postgres
POSTGRES_DB: pg-locks-monitor-test
POSTGRES_PASSWORD: secret
DATABASE_URL: postgresql://postgres:secret@localhost:5432/pg-locks-monitor-test
run: |
bundle exec rake test_all
bundle exec rspec spec
- name: Run tests for PG 12
env:
PG_VERSION: 12
Expand All @@ -93,7 +91,7 @@ jobs:
POSTGRES_PASSWORD: secret
DATABASE_URL: postgresql://postgres:secret@localhost:5433/pg-locks-monitor-test
run: |
bundle exec rake test_all
bundle exec rspec spec
- name: Run tests for PG 13
env:
PG_VERSION: 13
Expand All @@ -103,7 +101,7 @@ jobs:
POSTGRES_PASSWORD: secret
DATABASE_URL: postgresql://postgres:secret@localhost:5434/pg-locks-monitor-test
run: |
bundle exec rake test_all
bundle exec rspec spec
- name: Run tests for PG 14
env:
PG_VERSION: 14
Expand All @@ -113,7 +111,7 @@ jobs:
POSTGRES_PASSWORD: secret
DATABASE_URL: postgresql://postgres:secret@localhost:5435/pg-locks-monitor-test
run: |
bundle exec rake test_all
bundle exec rspec spec
- name: Run tests for PG 15
env:
PG_VERSION: 15
Expand All @@ -123,5 +121,17 @@ jobs:
POSTGRES_PASSWORD: secret
DATABASE_URL: postgresql://postgres:secret@localhost:5436/pg-locks-monitor-test
run: |
bundle exec rake test_all
bundle exec rspec spec
- name: Run tests for PG 16
env:
PG_VERSION: 15
POSTGRES_HOST: localhost
POSTGRES_USER: postgres
POSTGRES_DB: pg-locks-monitor-test
POSTGRES_PASSWORD: secret
DATABASE_URL: postgresql://postgres:secret@localhost:5437/pg-locks-monitor-test
run: |
bundle exec rspec spec
2 changes: 1 addition & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ RSpec::Core::RakeTask.new(:spec)

desc "Test all PG versions"
task :test_all do
system("PG_VERSION=11 bundle exec rspec spec/ && PG_VERSION=12 bundle exec rspec spec/ && PG_VERSION=13 bundle exec rspec spec/ && PG_VERSION=14 bundle exec rspec spec/")
system("PG_VERSION=11 bundle exec rspec spec && PG_VERSION=12 bundle exec rspec spec && PG_VERSION=13 bundle exec rspec spec && PG_VERSION=14 bundle exec rspec spec && PG_VERSION=16 bundle exec rspec spec && PG_VERSION=16 bundle exec rspec spec")
end
2 changes: 0 additions & 2 deletions docker-compose.yml.sample
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '3'

services:
postgres11:
image: postgres:11.16-alpine
Expand Down
25 changes: 23 additions & 2 deletions lib/pg-locks-monitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def self.snapshot!
in_format: :hash,
).select do |lock|
if (age = lock.fetch("age"))
(ActiveSupport::Duration.parse(age).to_f * 1000) > configuration.locks_min_duration_ms
DurationHelper.parse_to_ms(age) > configuration.locks_min_duration_ms
end
end.select(&configuration.locks_filter_proc)
.first(configuration.locks_limit)
Expand All @@ -20,13 +20,18 @@ def self.snapshot!
end

blocking = RubyPgExtras.blocking(in_format: :hash).select do |block|
(ActiveSupport::Duration.parse(block.fetch("blocking_duration")).to_f * 1000) > configuration.blocking_min_duration_ms
DurationHelper.parse_to_ms(block.fetch("blocking_duration")) > configuration.blocking_min_duration_ms
end.select(&configuration.blocking_filter_proc)
.first(configuration.locks_limit)

if blocking.count > 0 && configuration.monitor_blocking
configuration.notifier_class.call(blocking)
end

{
locks: locks,
blocking: blocking,
}
end

def self.configuration
Expand All @@ -36,6 +41,22 @@ def self.configuration
def self.configure
yield(configuration)
end

class DurationHelper
require "date"

def self.parse_to_ms(duration_str)
time = DateTime.strptime(duration_str, "%H:%M:%S.%N")
hours = time.hour
minutes = time.minute
seconds = time.second
nanoseconds = time.second_fraction * (10 ** 9)

total_ms = (hours * 3600 * 1000) + (minutes * 60 * 1000) + (seconds * 1000) + (nanoseconds / 1_000_000).to_i

total_ms
end
end
end

require "pg_locks_monitor/default_notifier"
Expand Down
2 changes: 1 addition & 1 deletion lib/pg_locks_monitor/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module PgLocksMonitor
VERSION = "0.2.2"
VERSION = "0.3.0"
end
34 changes: 19 additions & 15 deletions spec/default_notifier_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,6 @@
require "spec_helper"

describe PgLocksMonitor::DefaultNotifier do
before do
# Mock Rails and its logger
Rails = nil
logger_double = double("Logger")
allow(logger_double).to receive(:info)
allow(Rails).to receive(:logger).and_return(logger_double)
end

it "requires correct config if Slack notifications enabled" do
expect {
PgLocksMonitor::DefaultNotifier.call({})
Expand All @@ -24,14 +16,26 @@
}.to raise_error(RuntimeError)
end

it "sends the Slack notification if enabled" do
PgLocksMonitor.configure do |config|
config.notify_slack = true
config.slack_webhook_url = "https://hooks.slack.com/services/123456789/123456789/123456789"
config.slack_channel = "pg-locks-monitor"
describe "Slack notification enabled" do
before do
PgLocksMonitor.configure do |config|
config.notify_slack = true
config.slack_webhook_url = "https://hooks.slack.com/services/123456789/123456789/123456789"
config.slack_channel = "pg-locks-monitor"
end
end

expect_any_instance_of(Slack::Notifier).to receive(:ping)
PgLocksMonitor::DefaultNotifier.call({ locks: "data" })
after do
PgLocksMonitor.configure do |config|
config.notify_slack = false
config.slack_webhook_url = nil
config.slack_channel = nil
end
end

it "sends the Slack notification" do
expect_any_instance_of(Slack::Notifier).to receive(:ping)
PgLocksMonitor::DefaultNotifier.call({ locks: "data" })
end
end
end
22 changes: 22 additions & 0 deletions spec/smoke_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,33 @@
require "spec_helper"

describe PgLocksMonitor do
def spawn_update
Thread.new do
conn = PG.connect(ENV["DATABASE_URL"])
conn.exec("
BEGIN;
UPDATE pg_locks_monitor_users SET name = 'Updated';
select pg_sleep(2);
COMMIT;
")
end
end

describe "snapshot!" do
it "works" do
expect {
PgLocksMonitor.snapshot!
}.not_to raise_error
end

it "returns correct locks data" do
spawn_update
spawn_update
sleep 1

result = PgLocksMonitor.snapshot!
expect(result.fetch(:locks).count).to eq(5)
expect(result.fetch(:blocking).count).to eq(1)
end
end
end
27 changes: 27 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,35 @@
"5434"
elsif pg_version == "14"
"5435"
elsif pg_version == "15"
"5436"
elsif pg_version == "16"
"5437"
else
"5432"
end

ENV["DATABASE_URL"] ||= "postgresql://postgres:secret@localhost:#{port}/pg-locks-monitor-test"

RSpec.configure do |config|
Rails = {}

config.before(:each) do
# Mock Rails and its logger
logger_double = double("Logger")
allow(logger_double).to receive(:info)
allow(Rails).to receive(:logger).and_return(logger_double)
end

config.before(:suite) do
conn = RubyPgExtras.connection
conn.exec("CREATE TABLE IF NOT EXISTS pg_locks_monitor_users (id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL);")
conn.exec("INSERT INTO pg_locks_monitor_users (name) VALUES ('Alice');")
conn.exec("INSERT INTO pg_locks_monitor_users (name) VALUES ('Bob');")
end

config.after(:suite) do
conn = RubyPgExtras.connection
conn.exec("DROP TABLE IF EXISTS pg_locks_monitor_users;")
end
end

0 comments on commit 1e032f8

Please sign in to comment.