Skip to content

Instantly share code, notes, and snippets.

@julik
Last active June 24, 2019 23:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save julik/1e405b94015c20e82751d4ac6b0dd1fe to your computer and use it in GitHub Desktop.
Save julik/1e405b94015c20e82751d4ac6b0dd1fe to your computer and use it in GitHub Desktop.
Visualser for orderings of given events. When your jobs can execute concurrently and you are faced with weirdo concurrency bugs in time, this might help visualise the variants of execution order. Especially when multiple tasks might be executing at the same time but actually depend on each other's output, and your execution engine cannot enforce…
require 'set'
class Eventuality
def initialize
@events = Set.new
@invariants = []
@tasks = []
end
def add_single_event(evt)
@events << evt
end
def add_task_with_start_and_finish(start_evt, finish_evt)
@events << start_evt << finish_evt
@tasks << [start_evt, finish_evt]
invariant_sequencing(start_evt, finish_evt)
end
def invariant_sequencing(event_that_must_happen_first, event_that_must_happen_later)
@invariants << ->(seq) {
seq.index(event_that_must_happen_first) < seq.index(event_that_must_happen_later)
}
end
def invariant_pos(of_event, requisite_position)
@invariants << ->(seq) {
seq.index(of_event) == requisite_position
}
end
def print_stats
n_seqs = possible_sequences.length
puts "With #{@events.length} possible events there are #{n_seqs} possible orderings"
end
def possible_sequences
@events.to_a.permutation.select do |possible_operation_sequence|
@invariants.all? {|inv| inv.(possible_operation_sequence) }
end
end
def print_swimlanes(to = $stdout)
seqs = possible_sequences
total_permutations = @events.to_a.permutation.to_a.length
to.puts "Laying out #{seqs.length} possible orderings of #{@events.length} events"
to.puts "#{total_permutations - seqs.length} orderings were rejected as they did not satisfy all invariants."
to.puts "#{@invariants.length} invariants were honored"
seqs.each do |seq|
print_swimlanes_for_sequence(seq, to)
end
end
def print_swimlanes_for_sequence(seq, to = $stdout)
swimlanes = []
sep = ' '
@tasks.each do |(start, finish)|
a_idx = seq.index(start)
b_idx = seq.index(finish)
next unless a_idx && b_idx
pre_indent = sep * seq[0...a_idx].join.length
connector = "-" * seq[a_idx...(b_idx - 1)].join(sep).length
lane = seq.map.with_index do |el, idx|
if idx < a_idx || idx > b_idx
' ' * (el.to_s.length + 1)
elsif idx == a_idx || idx == b_idx
el
else
'-' * (el.to_s.length + 1)
end
end
if b_idx == a_idx + 1
lane.insert(a_idx + 1, '---')
end
swimlanes << lane
seq[a_idx] = sep * start.to_s.length
seq[b_idx] = sep * finish.to_s.length
end
to.puts ''
to.puts seq.join(sep)
swimlanes.each do |lane|
to.puts lane.join('')
end
end
end
eventuality = Eventuality.new
eventuality.add_single_event(:part_put)
eventuality.add_task_with_start_and_finish(:start_handle_part_job, :finish_handle_part_job)
eventuality.add_task_with_start_and_finish(:start_splice_job, :finish_splice_job)
eventuality.invariant_pos(:part_put, 0)
eventuality.print_swimlanes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment