A tool for measuring the coverage of Bluesky/ATProto relays
9
fork

Configure Feed

Select the types of activity you want to include in your feed.

extracted start_time & duration to a new TestRun model

+89 -11
+13
db/migrate/20260414211938_add_test_runs.rb
··· 1 + class AddTestRuns < ActiveRecord::Migration[7.2] 2 + def change 3 + create_table :test_runs do |t| 4 + t.datetime :start_time, precision: 0, null: false 5 + t.integer :duration, null: false 6 + end 7 + 8 + add_index :test_runs, :start_time 9 + 10 + add_column :reports, :test_run_id, :smallint, null: true 11 + add_index :reports, :test_run_id 12 + end 13 + end
+46
db/migrate/20260414212524_migrate_to_test_runs.rb
··· 1 + require_relative '../../report' 2 + require_relative '../../test_run' 3 + 4 + class MigrateToTestRuns < ActiveRecord::Migration[7.2] 5 + def up 6 + ActiveRecord::Base.logger = Logger.new(STDOUT) 7 + 8 + Report.group('start_time, duration').select('start_time, duration').each do |t| 9 + test = TestRun.create!(start_time: t.start_time, duration: t.duration) 10 + 11 + Report.where(start_time: t.start_time, duration: t.duration).each do |r| 12 + r.update!(test_run: test) 13 + end 14 + end 15 + 16 + change_column_null :reports, :test_run_id, false 17 + 18 + change_table :reports do |t| 19 + t.remove :start_time 20 + t.remove :duration 21 + end 22 + 23 + ActiveRecord::Base.logger = nil 24 + end 25 + 26 + def down 27 + ActiveRecord::Base.logger = Logger.new(STDOUT) 28 + 29 + change_table :reports do |t| 30 + t.datetime :start_time, precision: 0 31 + t.integer :duration 32 + end 33 + 34 + Report.includes(:test_run).each do |r| 35 + r.update!(start_time: r.test_run.start_time, duration: r.test_run.duration) 36 + end 37 + 38 + change_column_null :reports, :start_time, false 39 + change_column_null :reports, :duration, false 40 + change_column_null :reports, :test_run_id, true 41 + 42 + TestRun.delete_all 43 + 44 + ActiveRecord::Base.logger = nil 45 + end 46 + end
+9 -4
db/schema.rb
··· 10 10 # 11 11 # It's strongly recommended that you check this file into your version control system. 12 12 13 - ActiveRecord::Schema[7.2].define(version: 2026_03_23_175315) do 13 + ActiveRecord::Schema[7.2].define(version: 2026_04_14_212524) do 14 14 create_table "reports", force: :cascade do |t| 15 - t.datetime "start_time", precision: 0, null: false 16 15 t.string "host", null: false 17 - t.integer "duration", null: false 18 16 t.integer "users", null: false 19 17 t.integer "events", null: false 20 18 t.string "source_type", null: false 21 19 t.boolean "connected", null: false 22 20 t.integer "error_count", default: 0, null: false 23 21 t.integer "reconnect_count", default: 0, null: false 24 - t.index ["start_time"], name: "index_reports_on_start_time" 22 + t.integer "test_run_id", null: false 23 + t.index ["test_run_id"], name: "index_reports_on_test_run_id" 24 + end 25 + 26 + create_table "test_runs", force: :cascade do |t| 27 + t.datetime "start_time", precision: 0, null: false 28 + t.integer "duration", null: false 29 + t.index ["start_time"], name: "index_test_runs_on_start_time" 25 30 end 26 31 end
+4 -1
report.rb
··· 1 + require_relative 'test_run' 2 + 1 3 class Report < ActiveRecord::Base 2 4 enum :source_type, { 3 5 jetstream: 'jetstream', 4 6 firehose: 'firehose' 5 7 } 6 8 7 - validates_presence_of :start_time, :host, :duration, :users, :events, :source_type 9 + validates_presence_of :host, :users, :events, :source_type 10 + belongs_to :test_run 8 11 end
+6 -3
run_test.rb
··· 8 8 require_relative 'init' 9 9 require_relative 'opts' 10 10 require_relative 'report' 11 + require_relative 'test_run' 11 12 12 13 def get_hosts(entries) 13 14 if entries ··· 89 90 90 91 Process.kill('SIGINT', *workers.map(&:pid)) 91 92 93 + unless options[:dont_save] 94 + test = TestRun.create!(start_time: test_start_time, duration: duration) 95 + end 96 + 92 97 while !workers.empty? 93 98 pid = Process.wait 94 99 worker = workers.detect { |w| w.pid == pid } ··· 101 106 puts "#{worker.host}: #{result.inspect}" if verbose 102 107 103 108 unless options[:dont_save] 104 - Report.create!( 105 - start_time: test_start_time, 106 - duration: duration, 109 + test.reports.create!( 107 110 host: worker.host, 108 111 source_type: worker.type, 109 112 users: result['users'],
+3 -2
server.rb
··· 8 8 9 9 require_relative 'init' 10 10 require_relative 'report' 11 + require_relative 'test_run' 11 12 12 13 $config = YAML.load(File.read(SOURCES)) 13 14 $owners = ($config['relays'] + $config['jetstreams']).map { |x| x.is_a?(Hash) ? [x['host'], x] : [x, nil] }.then { Hash[it] } ··· 33 34 end 34 35 35 36 get '/' do 36 - @last_test = Report.maximum(:start_time) 37 + @last_test = TestRun.order(:start_time).last 37 38 38 39 if @last_test 39 - @reports = Report.where(start_time: @last_test).order('users DESC, connected DESC').to_a 40 + @reports = @last_test.reports.order('users DESC, connected DESC').to_a 40 41 41 42 if @reports.length >= 3 42 43 middle = @reports.length / 2
+7
test_run.rb
··· 1 + require_relative 'report' 2 + 3 + class TestRun < ActiveRecord::Base 4 + validates_presence_of :start_time, :duration 5 + 6 + has_many :reports, dependent: :delete_all 7 + end
+1 -1
views/index.erb
··· 3 3 <h2>atproto relay observatory</h2> 4 4 5 5 <% if @last_test %> 6 - <p class="last-update">Latest observation: <%= @last_test %> (<%= @reports.first.duration %>s)</p> 6 + <p class="last-update">Latest observation: <%= @last_test.start_time %> (<%= @last_test.duration %>s)</p> 7 7 8 8 <table id="results"> 9 9 <tr>