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
26 changes: 26 additions & 0 deletions .github/workflows/ruby.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Ruby

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
ruby-version: ['3.1', '3.2', '3.3', '3.4']

steps:
- uses: actions/checkout@v4

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: true

- name: Run tests
run: bin/rspec
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Or install it yourself as:

## Usage

### GraphQL Controller

```ruby
class GraphqlController < ApplicationController
include NexlShared::GraphqlControllerConcern
Expand All @@ -33,6 +35,50 @@ Or install it yourself as:
end
```

### CheckQueueLatency

`CheckQueueLatency` monitors Sidekiq queue latency and reports errors when queues exceed acceptable thresholds.

#### Queue Types

**Standard Queues**: Queues without special naming conventions are checked against a 3-hour threshold (excluding 'imports' and 'maintenance' queues).

**Within Queues**: Queues following the `within_*` naming convention have dynamic thresholds based on their name:
- `within_a_minute` → 60 seconds
- `within_5_minutes` → 5 minutes
- `within_a_hour` → 1 hour

#### Error Threshold

By default, errors are tracked when queue latency reaches **150% of the threshold** (50% above). For example:
- `within_a_minute` → errors at 90 seconds or more
- `within_5_minutes` → errors at 7.5 minutes or more
- `within_a_hour` → errors at 90 minutes or more

This threshold multiplier can be configured:

```ruby
# config/initializers/nexl_shared.rb
NexlShared.queue_latency_error_threshold = 2.0 # Errors at 200% of threshold
```

#### Usage

Run periodically (e.g., via cron or scheduled job):

```ruby
NexlShared::CheckQueueLatency.perform_now
```

Or with custom queues and error tracker:

```ruby
NexlShared::CheckQueueLatency.new.perform(
queues: Sidekiq::Queue.all,
error_tracker: Rollbar
)
```

## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
Expand Down
9 changes: 8 additions & 1 deletion lib/nexl_shared.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@

module NexlShared
class Error < StandardError; end
# Your code goes here...

class << self
attr_accessor :queue_latency_error_threshold

def queue_latency_error_threshold
@queue_latency_error_threshold ||= 1.5
end
end
end

require 'nexl_shared/error_tracker'
Expand Down
3 changes: 2 additions & 1 deletion lib/nexl_shared/check_queue_latency.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ def check_latency(queue, error_tracker)
return if unit.nil? || !amount.respond_to?(unit)

queue_latency = queue.latency.seconds
return if queue_latency <= amount.public_send(unit)
threshold = amount.public_send(unit) * NexlShared.queue_latency_error_threshold
return if queue_latency < threshold

track_error(error_tracker, queue_latency, queue.name)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/nexl_shared/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module NexlShared
VERSION = "0.3.1"
VERSION = "0.4.0"
end
12 changes: 6 additions & 6 deletions spec/nexl_shared/check_queue_latency_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ module NexlShared
let(:queue_name) { 'within_a_minute' }

context 'when latency is too high' do
let(:queue_latency) { 2.minutes.to_i }
let(:queue_latency) { 90.seconds.to_i }

it { expect(error_tracker).to have_received(:critical) }
end

context 'when latency is acceptable' do
let(:queue_latency) { 1.minute.to_i }
let(:queue_latency) { 89.seconds.to_i }

it { expect(error_tracker).not_to have_received(:critical) }
end
Expand All @@ -33,13 +33,13 @@ module NexlShared
let(:queue_name) { 'within_5_minutes' }

context 'when latency is too high' do
let(:queue_latency) { 6.minutes.to_i }
let(:queue_latency) { 450.seconds.to_i }

it { expect(error_tracker).to have_received(:critical) }
end

context 'when latency is acceptable' do
let(:queue_latency) { 5.minutes.to_i }
let(:queue_latency) { 449.seconds.to_i }

it { expect(error_tracker).not_to have_received(:critical) }
end
Expand All @@ -49,13 +49,13 @@ module NexlShared
let(:queue_name) { 'within_a_hour' }

context 'when latency is too high' do
let(:queue_latency) { 61.minutes.to_i }
let(:queue_latency) { 90.minutes.to_i }

it { expect(error_tracker).to have_received(:critical) }
end

context 'when latency is acceptable' do
let(:queue_latency) { 1.hour.to_i }
let(:queue_latency) { 89.minutes.to_i }

it { expect(error_tracker).not_to have_received(:critical) }
end
Expand Down