Skip to content

Commit

Permalink
syncer
Browse files Browse the repository at this point in the history
  • Loading branch information
dpep committed Nov 27, 2023
1 parent a1bc9c4 commit 33d878c
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 72 deletions.
23 changes: 3 additions & 20 deletions lib/network_resiliency.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require "network_resiliency/refinements"
require "network_resiliency/stats"
require "network_resiliency/stats_engine"
require "network_resiliency/syncer"
require "network_resiliency/version"

using NetworkResiliency::Refinements
Expand All @@ -24,7 +25,7 @@ module Adapter
def configure
yield self if block_given?

start_syncing if redis
Syncer.start(redis) if redis
end

def patch(*adapters)
Expand Down Expand Up @@ -292,30 +293,12 @@ def reset
@mode = nil
Thread.current["network_resiliency"] = nil
StatsEngine.reset

if @sync_worker
@sync_worker.kill
@sync_worker = nil
end
Syncer.stop
end

# private

def thread_state
Thread.current["network_resiliency"] ||= {}
end

def start_syncing
@sync_worker.kill if @sync_worker

raise "Redis not configured" unless redis

@sync_worker = Thread.new do
loop do
StatsEngine.sync(redis)

sleep(3)
end
end
end
end
39 changes: 39 additions & 0 deletions lib/network_resiliency/syncer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module NetworkResiliency
class Syncer < Thread
class << self
def start(redis)
@instance&.shutdown
@instance = new(redis)
end

def stop
@instance&.shutdown
@instance = nil
end
end

def initialize(redis)
@redis = redis

super { sync }
end

def shutdown
@shutdown = true

# prevent needless delay
raise Interrupt if status == "sleep"
end

private

def sync
until @shutdown
StatsEngine.sync(@redis)

sleep(3)
end
rescue Interrupt
end
end
end
69 changes: 18 additions & 51 deletions spec/network_resiliency_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,20 @@ def expect_enabled
expect_enabled.to be false
expect(NetworkResiliency.mode).to be :resilient
end

it "will start syncing" do
NetworkResiliency.configure
expect(NetworkResiliency::Syncer).to have_received(:start)
end

context "when Redis is not configured" do
before { NetworkResiliency.redis = nil }

it "will not start syncing" do
NetworkResiliency.configure
expect(NetworkResiliency::Syncer).not_to have_received(:start)
end
end
end

describe ".mode" do
Expand Down Expand Up @@ -617,57 +631,10 @@ def expect_enabled
end
end

describe ".start_syncing" do
before do
# mocking not supported in Threads
NetworkResiliency.statsd = nil

# unstub from spec_helper
allow(NetworkResiliency).to receive(:start_syncing).and_call_original
allow(NetworkResiliency::StatsEngine).to receive(:sync)
end

context "when Redis is configured" do
it { expect(NetworkResiliency.redis).to be }

it "will be called by .configure" do
NetworkResiliency.configure
expect(NetworkResiliency).to have_received(:start_syncing)
end
end

it "can be called many times without error" do
3.times { NetworkResiliency.send :start_syncing }
end

it "will stop previous workers so only one is running at a time" do
threads = Thread.list

workers = 3.times.map do
NetworkResiliency.send(:start_syncing)
NetworkResiliency.instance_variable_get(:@sync_worker)
end

workers.first(2).each { |w| w.join }

expect(Thread.list - threads).to contain_exactly(
NetworkResiliency.instance_variable_get(:@sync_worker),
)
end

context "when Redis is not configured" do
before { NetworkResiliency.redis = nil }

it "raises an error" do
expect {
NetworkResiliency.send :start_syncing
}.to raise_error(/Redis/)
end

it "does not get called from .configure" do
NetworkResiliency.configure
expect(NetworkResiliency).not_to have_received(:start_syncing)
end
describe ".reset" do
it "stop syncing" do
expect(NetworkResiliency::Syncer).to receive(:stop)
described_class.reset
end
end
end
2 changes: 1 addition & 1 deletion spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
end

# disable background sync
allow(NetworkResiliency).to receive(:start_syncing)
allow(NetworkResiliency::Syncer).to receive(:start)

# since Timecop doesn't work with Process.clock_gettime
allow(Process).to receive(:clock_gettime).and_return(*(1..1_000))
Expand Down
47 changes: 47 additions & 0 deletions spec/syncer_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
describe NetworkResiliency::Syncer do
before do
# mocking not supported in Threads
NetworkResiliency.statsd = nil

# unstub from spec_helper
allow(NetworkResiliency::Syncer).to receive(:start).and_call_original
end

let(:redis) { Redis.new }

def start
described_class.start(redis)
end

describe ".start" do
it "returns a Thread" do
expect(start).to be_a(Thread)
end

it "can be called many times without error" do
3.times { start }
end

it "will stop previous workers so only one is running at a time" do
first_worker = start
second_worker = start

first_worker.join

expect(first_worker).not_to be_alive
expect(second_worker).to be_alive
end
end

describe ".stop" do
it "stops syncing" do
worker = start
expect(worker).to be_alive

described_class.stop

worker.join
expect(worker).not_to be_alive
end
end
end

0 comments on commit 33d878c

Please sign in to comment.