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: 2 additions & 0 deletions CHANGELOG.markdown
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#### Unreleased
* Add a check for SolidCache
> stevenchanin & sidk: https://github.com/emmahsax/okcomputer/pull/15
* Update MIT license copyright holder
> emmahsax: https://github.com/emmahsax/okcomputer/pull/10
* Added `bin/release` script to make it easier to make new version bumps and releases without other tools like soyuz or octopolo
Expand Down
3 changes: 3 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ OkComputer::Registry.register "resque_backed_up", OkComputer::ResqueBackedUpChec

# This check works on 2.4.0 and above versions of resque-scheduler
OkComputer::Registry.register "resque_scheduler_down", OkComputer::ResqueSchedulerCheck.new

# If you're using SolidCache instead of Memcached, use this check instead of CacheCheck
OkComputer::Registry.register "cache", OkComputer::CacheCheckSolidCache.new
```

### Registering Custom Checks
Expand Down
49 changes: 49 additions & 0 deletions lib/ok_computer/built_in_checks/cache_check_solid_cache.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
module OkComputer
# Verifies that Rails cache is set up and can speak with SolidCache
class CacheCheckSolidCache < Check
# Public: Check whether the cache is active
def check
mark_message "Cache is available (#{stats})"
rescue ConnectionFailed => e
mark_failure
mark_message "Error: '#{e}'"
end

# Public: Outputs stats string for cache
def stats
return "" unless Rails.cache.respond_to? :stats

stats = Rails.cache.stats
connection_stats = stats[:connection_stats][SolidCache::Entry.current_shard]

max_entries = connection_stats[:max_entries]
current_entries = connection_stats[:entries]
max_age = connection_stats[:max_age]
oldest_age = connection_stats[:oldest_age]

age_text = if oldest_age
"oldest: #{format_duration(oldest_age)}, max: #{format_duration(max_age)}"
else
"no entries"
end

"entries: #{current_entries}/#{max_entries}, #{age_text}, #{stats[:connections]} connections"
rescue => e
raise ConnectionFailed, e
end

private

def format_duration(seconds)
if seconds < 60
"#{seconds.round}s"
elsif seconds < 3600
"#{(seconds / 60).round}m"
else
"#{(seconds / 3600).round}h"
end
end

ConnectionFailed = Class.new(StandardError)
end
end
1 change: 1 addition & 0 deletions lib/okcomputer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
require "ok_computer/built_in_checks/active_record_migrations_check"
require "ok_computer/built_in_checks/app_version_check"
require "ok_computer/built_in_checks/cache_check"
require "ok_computer/built_in_checks/cache_check_solid_cache"
require "ok_computer/built_in_checks/default_check"
require "ok_computer/built_in_checks/delayed_job_backed_up_check"
require "ok_computer/built_in_checks/directory_check"
Expand Down
105 changes: 105 additions & 0 deletions spec/ok_computer/built_in_checks/cache_check_solid_cache_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
require "rails_helper"

module OkComputer
describe CacheCheckSolidCache do
let(:stats) do
{
connections: 2,
connection_stats: {
"shard1" => {
max_age: 3600,
oldest_age: 1800,
max_entries: 1000,
entries: 500
}
}
}
end

before do
stub_const("SolidCache::Entry", Class.new)
allow(SolidCache::Entry).to receive(:current_shard).and_return("shard1")
end

it "is a Check" do
expect(subject).to be_a Check
end

context "#check" do
let(:error_message) { "Error message" }

context "with a successful connection" do
before do
expect(subject).to receive(:stats).and_return("test stats")
end

it { is_expected.to be_successful_check }
it { is_expected.to have_message "Cache is available (test stats)" }
end

context "with an unsuccessful connection" do
before do
expect(subject).to receive(:stats).and_raise(CacheCheckSolidCache::ConnectionFailed, error_message)
end

it { is_expected.not_to be_successful_check }
it { is_expected.to have_message "Error: '#{error_message}'" }
end
end

context "#stats" do
context "when can connect to cache" do
before do
allow(Rails).to receive_message_chain(:cache, :stats).and_return(stats)
end

it "returns a formatted stats string" do
expect(subject.stats).to eq "entries: 500/1000, oldest: 30m, max: 1h, 2 connections"
end
end

context "when no entries exist" do
before do
stats[:connection_stats]["shard1"][:oldest_age] = nil
allow(Rails).to receive_message_chain(:cache, :stats).and_return(stats)
end

it "indicates no entries exist" do
expect(subject.stats).to eq "entries: 500/1000, no entries, 2 connections"
end
end

context "when cannot connect to cache" do
before do
allow(Rails).to receive_message_chain(:cache, :stats).and_raise("broken")
end

it { expect { subject.stats }.to raise_error(CacheCheckSolidCache::ConnectionFailed) }
end

context "when using a cache without stats" do
before do
allow(Rails.cache).to receive(:respond_to?).with(:stats).and_return(false)
end

it "returns an empty string" do
expect(subject.stats).to eq ""
end
end
end

context "#format_duration" do
it "formats seconds" do
expect(subject.send(:format_duration, 45)).to eq "45s"
end

it "formats minutes" do
expect(subject.send(:format_duration, 180)).to eq "3m"
end

it "formats hours" do
expect(subject.send(:format_duration, 7200)).to eq "2h"
end
end
end
end