Last active
June 24, 2019 23:13
-
-
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…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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