Skip to content

Commit

Permalink
add more tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
ethowitz committed Jan 19, 2024
1 parent 4708df2 commit 44d0a78
Show file tree
Hide file tree
Showing 5 changed files with 337 additions and 59 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# rspec failure tracking
.rspec_status
Gemfile.lock
spec/internal/db/readyset_caches.rb
spec/internal/tmp/
spec/internal/config/storage.yml
spec/internal/db/*.sqlite
93 changes: 83 additions & 10 deletions lib/tasks/readyset.rake
Original file line number Diff line number Diff line change
@@ -1,20 +1,93 @@
# lib/tasks/readyset.rake
require 'colorize'
require 'erb'
require 'progressbar'
require 'terminal-table'

namespace :readyset do
desc 'Creates caches for all of the supported queries on ReadySet'
task cache_supported_queries: :environment do
Readyset::Query.cache_all_supported!
desc 'Prints a list of all the queries that ReadySet has proxied'
task proxied_queries: :environment do
Rails.application.eager_load!

rows = Readyset::Query::ProxiedQuery.all.map do |q|
[q.id, q.text, q.supported, q.count]
end
table = Terminal::Table.new(headings: [:id, :text, :supported, :count], rows: rows)

puts table
end

desc 'Drops all the caches on ReadySet'
task drop_all_caches: :environment do
Readyset::Query.drop_all_caches!
namespace :proxied_queries do
desc 'Creates caches for all of the supported queries on ReadySet'
task cache_all_supported: :environment do
Rails.application.eager_load!

Readyset::Query::ProxiedQuery.cache_all_supported!
end

desc 'Clears the list of proxied queries on ReadySet'
task drop_all: :environment do
Rails.application.eager_load!

Readyset.raw_query('DROP ALL PROXIED QUERIES')
end
end

desc 'Prints a list of all the cached queries on ReadySet'
task all_caches: :environment do
Readyset::Query.all_cached.each do |query|
puts query.inspect
task caches: :environment do
Rails.application.eager_load!

rows = Readyset::Query::CachedQuery.all.map do |q|
[q.id, q.name, q.text, q.always, q.count]
end
table = Terminal::Table.new(headings: [:id, :name, :text, :always, :count], rows: rows)

puts table
end

namespace :caches do
desc 'Drops the cache with the given name'
task :drop, [:name] => :environment do |_, args|
Rails.application.eager_load!

if args.first
Readyset.drop_cache!(args.first)
else
puts 'A cache name must be passed to this task'
end
end

desc 'Drops all the caches on ReadySet'
task drop_all: :environment do
Rails.application.eager_load!

Readyset::Query::CachedQuery.drop_all!
end

desc 'Dumps the set of caches that currently exist on ReadySet to a file'
task dump: :environment do
Rails.application.eager_load!
end
end

desc 'Prints status information about ReadySet'
task status: :environment do
Rails.application.eager_load!

rows = Readyset.raw_query('SHOW READYSET STATUS').
map { |result| [result['name'], result['value']] }
table = Terminal::Table.new(rows: rows)

puts table
end

desc 'Prints information about the tables known to ReadySet'
task tables: :environment do
Rails.application.eager_load!

rows = Readyset.raw_query('SHOW READYSET TABLES').
map { |result| [result['table'], result['status'], result['description']] }
table = Terminal::Table.new(headings: [:table, :status, :description], rows: rows)

puts table
end
end
1 change: 1 addition & 0 deletions readyset.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Gem::Specification.new do |spec|
spec.add_dependency 'activerecord', '>= 6.1'
spec.add_dependency 'activesupport', '>= 6.1'
spec.add_dependency 'rake', '~> 13.0'
spec.add_dependency 'terminal-table', '~> 3.0'

spec.add_development_dependency 'combustion', '~> 1.3'
spec.add_development_dependency 'factory_bot', '~> 6.4'
Expand Down
252 changes: 252 additions & 0 deletions spec/rake_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
require 'colorize'
require 'rake'
require 'spec_helper'

load './lib/tasks/readyset.rake'

RSpec.describe 'readyset.rake' do
before do
Rake::Task.define_task(:environment)
end

describe 'readyset' do
describe 'caches' do
it 'prints a table with the caches that currently exist on ReadySet' do
build_and_create_cache(:cached_query)

expected_message = <<~TABLE
+--------------------+--------------------+---------------------------------+--------+-------+
| id | name | text | always | count |
+--------------------+--------------------+---------------------------------+--------+-------+
| q_4f3fb9ad8f73bc0c | q_4f3fb9ad8f73bc0c | SELECT | false | 0 |
| | | "public"."cats"."breed" | | |
| | | FROM | | |
| | | "public"."cats" | | |
| | | WHERE | | |
| | | ("public"."cats"."name" = $1) | | |
+--------------------+--------------------+---------------------------------+--------+-------+
TABLE
expect { Rake::Task['readyset:caches'].execute }.to output(expected_message).to_stdout
end

describe 'drop' do
context 'when given a cache name as an argument' do
it 'removes the cache with the given name' do
cache = build_and_create_cache(:cached_query)

Rake::Task['readyset:caches:drop'].execute([cache.id])

caches = Readyset::Query::CachedQuery.all
expect(caches.size).to eq(0)
end
end

context 'when given no arguments' do
it 'prints an error message' do
expect { Rake::Task['readyset:caches:drop'].execute }.
to output("A cache name must be passed to this task\n").to_stdout
end
end
end

describe 'drop_all' do
it 'removes the cache with the given name' do
build_and_create_cache(:cached_query)
build_and_create_cache(:cached_query_2)

Rake::Task['readyset:caches:drop_all'].execute

caches = Readyset::Query::CachedQuery.all
expect(caches.size).to eq(0)
end
end

describe 'dump' do
it 'dumps the current set of caches to a migration file' do
cache_1 = build_and_create_cache(:cached_query)
cache_2 = build_and_create_cache(:cached_query_2)

Rake::Task['readyset:caches:dump'].execute

load './spec/internal/db/readyset_caches.rb'
subclasses = Readyset::Caches.subclasses
expect(subclasses.size).to eq(1)

caches = subclasses.first.caches.to_a
caches.sort_by!(&:id)

expect(caches.first.always).to eq(cache_1.always)
expect(caches.first.text).to eq(cache_1.text)
expect(caches.second.always).to eq(cache_2.always)
expect(caches.second.text).to eq(cache_2.text)
end
end

describe 'migrate' do
after(:each) do
if File.exist?('./spec/internal/db/readyset_caches.rb')
File.delete('./spec/internal/db/readyset_caches.rb')
end
end

context "when the migration file contains caches that don't exist on ReadySet" do
it "creates the caches in the migration file that don't exist on ReadySet" do
# Setup
cache_to_create = build(:cached_query_2)
generate_migration_file([build(:cached_query), cache_to_create])

allow(Readyset::Query::CachedQuery).to receive(:all).and_return([build(:cached_query)])
allow(Readyset).to receive(:create_cache!).with(id: cache_to_create.id)
allow(STDIN).to receive(:gets).and_return("y\n")

# Execute
Rake::Task['readyset:caches:migrate'].execute

# Verify
expect(Readyset).to have_received(:create_cache!).with(id: cache_to_create.id)
end

it 'prints the expected output' do
# Setup
cache_to_create = build(:cached_query_2)
generate_migration_file([build(:cached_query), cache_to_create])

allow(Readyset::Query::CachedQuery).to receive(:all).and_return([build(:cached_query)])
allow(Readyset).to receive(:create_cache!).with(id: cache_to_create.id)
allow(STDIN).to receive(:gets).and_return("y\n")

# Execute + Verify
expected_message = "#{'Dropping'.red} 0 caches and #{'creating'.green} 1 caches. " \
'Continue? (y/n) '
expect { Rake::Task['readyset:caches:migrate'].execute }.to output(expected_message).
to_stdout
end
end

context "when ReadySet has caches that don't exist in the migration file" do
it 'drops the caches that exist on ReadySet that are not in the migration file' do
# Setup
generate_migration_file([build(:cached_query)])

cache_to_drop = build(:cached_query_2)
allow(Readyset::Query::CachedQuery).to receive(:all).
and_return([build(:cached_query), cache_to_drop])
allow(Readyset).to receive(:drop_cache!).with(name_or_id: cache_to_drop.id)
allow(STDIN).to receive(:gets).and_return("y\n")

# Execute
Rake::Task['readyset:caches:migrate'].execute

# Verify
expect(Readyset).to have_received(:drop_cache!).with(name_or_id: cache_to_drop.id)
end

it 'prints the expected output' do
# Setup
generate_migration_file([build(:cached_query)])

cache_to_drop = build(:cached_query_2)
allow(Readyset::Query::CachedQuery).to receive(:all).
and_return([build(:cached_query), cache_to_drop])
allow(Readyset).to receive(:drop_cache!).with(name_or_id: cache_to_drop.id)
allow(STDIN).to receive(:gets).and_return("y\n")

# Execute + Verify
expected_message = "#{'Dropping'.red} 1 caches and #{'creating'.green} 0 caches. " \
'Continue? (y/n) '
expect { Rake::Task['readyset:caches:migrate'].execute }.to output(expected_message).
to_stdout
end
end

def generate_migration_file(caches)
allow(Readyset::Query::CachedQuery).to receive(:all).and_return(caches)
Rake::Task['readyset:caches:dump'].execute
allow(Readyset::Query::CachedQuery).to receive(:all).and_call_original
end
end
end

describe 'proxied_queries' do
it 'prints a table with the queries that ReadySet has proxied' do
build_and_execute_proxied_query(:proxied_query)

expected_message = Regexp.new <<~TABLE
\\+--------------------\\+------------------------\\+-----------\\+-------\\+
\\| id \\| text \\| supported \\| count \\|
\\+--------------------\\+------------------------\\+-----------\\+-------\\+
\\| q_4f3fb9ad8f73bc0c \\| SELECT \\| pending \\| \\d+[ ]*\\|
\\| \\| "cats"\\."breed" \\| \\| [ ]*\\|
\\| \\| FROM \\| \\| [ ]*\\|
\\| \\| "cats" \\| \\| [ ]*\\|
\\| \\| WHERE \\| \\| [ ]*\\|
\\| \\| \\("cats"\\."name" = \\$1\\) \\| \\| [ ]*\\|
\\+--------------------\\+------------------------\\+-----------\\+-------\\+
TABLE
expect { Rake::Task['readyset:proxied_queries'].execute }.to output(expected_message).
to_stdout
end

describe 'cache_all_supported' do
it 'creates caches for all queries proxied by ReadySet that are supported to be cached' do
build_and_execute_proxied_query(:proxied_query)
build_and_execute_proxied_query(:unsupported_proxied_query)

eventually do
Readyset::Query::ProxiedQuery.all.all? { |query| query.supported != :pending }
end

Rake::Task['readyset:proxied_queries:cache_all_supported'].execute

expect(Readyset::Query::CachedQuery.all).to eq([build(:cached_query)])
end
end

describe 'drop_all' do
it 'clears the list of proxied queries on ReadySet' do
build_and_execute_proxied_query(:proxied_query)
build_and_execute_proxied_query(:proxied_query_2)

Rake::Task['readyset:proxied_queries:drop_all'].execute

proxied = Readyset::Query::ProxiedQuery.all
expect(proxied).to be_empty
end
end
end

describe 'status' do
it "prints a table that shows ReadySet's status" do
expected_message = Regexp.new <<~TABLE
\\+----------------------------\\+------------------------\\+
\\| Database Connection \\| Connected[ ]*\\|
\\| Connection Count \\| \\d+[ ]*\\|
\\| Snapshot Status \\| Completed[ ]*\\|
\\| Maximum Replication Offset \\| \\([0-9A-F]{1,8}\\/[0-9A-F]{1,8}, [0-9A-F]{1,8}\\/[0-9A-F]{1,8}\\) \\|
\\| Minimum Replication Offset \\| \\([0-9A-F]{1,8}\\/[0-9A-F]{1,8}, [0-9A-F]{1,8}\\/[0-9A-F]{1,8}\\) \\|
\\| Last started Controller \\| \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}[ ]*\\|
\\| Last completed snapshot \\| \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}[ ]*\\|
\\| Last started replication \\| \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}[ ]*\\|
\\+----------------------------\\+------------------------\\+
TABLE
expect { Rake::Task['readyset:status'].execute }.to output(expected_message).
to_stdout
end
end

describe 'tables' do
it 'prints a table that shows the tables known to ReadySet' do
expected_message = Regexp.new <<~TABLE
\\+---------------------------------\\+-------------\\+-------------\\+
\\| table \\| status \\| description \\|
\\+---------------------------------\\+-------------\\+-------------\\+
(\\| "public"\\."[\\w]*"[ ]*\\| Snapshotted \\| \\|\n?)*
\\+---------------------------------\\+-------------\\+-------------\\+
TABLE

expect { Rake::Task['readyset:tables'].execute }.to output(expected_message).
to_stdout
end
end
end
end
Loading

0 comments on commit 44d0a78

Please sign in to comment.