Async::Service::ChaosKittySourceAsyncServiceChaosKittyScratch

class Scratch

Scratch randomly kills victim processes.

Like a cat scratching furniture, this chaos operation randomly terminates victim processes to test resilience and recovery.

Definitions

def initialize(interval: 60, probability: 0.1, signal: :TERM)

Create a new scratch chaos operation.

Signature

parameter interval Integer

How often to check for chaos opportunities.

parameter probability Float

Probability (0.0 to 1.0) of causing chaos on each check.

parameter signal Symbol

The signal to send when scratching.

Implementation

def initialize(interval: 60, probability: 0.1, signal: :TERM)
	@interval = interval
	@probability = probability
	@signal = signal
	@victims = Set.new.compare_by_identity
end

attr_reader :victims

Signature

attribute Set

The set of registered victims.

def register(chaos_controller)

Register a victim with the scratch chaos.

Signature

parameter chaos_controller ChaosController

The chaos controller for the victim.

Implementation

def register(chaos_controller)
	Console.debug(self, "😺 Registering victim for scratch chaos.", id: chaos_controller.id)
	@victims.add(chaos_controller)
end

def remove(chaos_controller)

Remove a victim from the scratch chaos.

Signature

parameter chaos_controller ChaosController

The chaos controller for the victim.

Implementation

def remove(chaos_controller)
	@victims.delete(chaos_controller)
end

def status

Get status for the scratch chaos.

Signature

returns Hash

Status including victim count and configuration.

Implementation

def status
	{
		scratch: {
			victims: @victims.size,
			probability: @probability,
			signal: @signal
		}
	}
end

def unleash_scratch

Unleash a scratch on a random victim.

Implementation

def unleash_scratch
	return if @victims.empty?
	
	# Pick a random victim
	victim = @victims.to_a.sample
	return unless victim
	
	# Check probability
	return unless rand < @probability
	
	process_id = victim.process_id
	return unless process_id
	
	Console.info(self, "😾 *SCRATCH* Taking down a victim!", id: victim.id, process_id: process_id, signal: @signal)
	
	begin
		Process.kill(@signal, process_id)
	rescue Errno::ESRCH
		Console.warn(self, "Process already gone!", process_id: process_id)
	rescue => error
		Console.error(self, "Failed to scratch victim!", process_id: process_id, exception: error)
	end
end

def run

Run the scratch chaos operation.

Signature

returns Async::Task

The task that is running the scratch chaos.

Implementation

def run
	Async do
		Loop.run(interval: @interval) do
			unleash_scratch
		end
	end
end