-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit adds two rake tasks: - `readyset:caches:dump`, which reads the caches that exist on the connected ReadySet instance and dumps them to a Ruby DSL file - `readyset:caches:migrate`, which reads the aforementioned Ruby file, create the caches from the file that don't exist on ReadySet, and drops the caches on ReadySet that don't exist in the file Closes #9
- Loading branch information
1 parent
422358b
commit 1f8e325
Showing
15 changed files
with
300 additions
and
8 deletions.
There are no files selected for viewing
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
module Readyset | ||
class Caches | ||
class << self | ||
attr_reader :caches | ||
end | ||
|
||
def self.cache(id:, always: false) | ||
@caches ||= Set.new | ||
|
||
query = yield | ||
|
||
@caches << Query::CachedQuery.new( | ||
id: id, | ||
text: query.strip, | ||
always: always, | ||
) | ||
end | ||
end | ||
end |
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
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
require 'colorize' | ||
require 'erb' | ||
require 'progressbar' | ||
|
||
namespace :readyset do | ||
namespace :caches do | ||
desc 'Dumps the set of caches that currently exist on ReadySet to a file' | ||
task dump: :environment do | ||
Rails.application.eager_load! | ||
|
||
template = File.read(File.join(File.dirname(__FILE__), '../templates/caches.rb.tt')) | ||
|
||
queries = Readyset::Query::CachedQuery.all | ||
f = File.new(Readyset.configuration.migration_path, 'w') | ||
f.write(ERB.new(template, trim_mode: '-').result(binding)) | ||
f.close | ||
end | ||
|
||
desc 'Synchronizes the caches on ReadySet such that the caches on ReadySet match those ' \ | ||
'listed in db/readyset_caches.rb' | ||
task migrate: :environment do | ||
Rails.application.eager_load! | ||
|
||
file = Readyset.configuration.migration_path | ||
|
||
# We load the definition of the `Readyset::Caches` subclass in the context of a | ||
# container object so we can be sure that we are never re-opening a previously-defined | ||
# subclass of `Readyset::Caches`. When the container object is garbage collected, the | ||
# definition of the `Readyset::Caches` subclass is garbage collected too | ||
container = Object.new | ||
container.instance_eval(File.read(file)) | ||
caches = container.singleton_class::ReadysetCaches.caches | ||
|
||
caches_on_readyset = Readyset::Query::CachedQuery.all.index_by(&:id) | ||
caches_on_readyset_ids = caches_on_readyset.keys.to_set | ||
|
||
caches_in_migration_file = caches.index_by(&:id) | ||
caches_in_migration_file_ids = caches_in_migration_file.keys.to_set | ||
|
||
to_drop_ids = caches_on_readyset_ids - caches_in_migration_file_ids | ||
to_create_ids = caches_in_migration_file_ids - caches_on_readyset_ids | ||
|
||
if to_drop_ids.size.positive? || to_create_ids.size.positive? | ||
dropping = 'Dropping'.red | ||
creating = 'creating'.green | ||
print "#{dropping} #{to_drop_ids.size} caches and #{creating} #{to_create_ids.size} " \ | ||
'caches. Continue? (y/n) ' | ||
$stdout.flush | ||
y_or_n = STDIN.gets.strip | ||
|
||
if y_or_n == 'y' | ||
if to_drop_ids.size.positive? | ||
bar = ProgressBar.create(title: 'Dropping caches', total: to_drop_ids.size) | ||
|
||
to_drop_ids.each do |id| | ||
bar.increment | ||
Readyset.drop_cache!(name_or_id: id) | ||
end | ||
end | ||
|
||
if to_create_ids.size.positive? | ||
bar = ProgressBar.create(title: 'Creating caches', total: to_create_ids.size) | ||
|
||
to_create_ids.each do |id| | ||
bar.increment | ||
Readyset.create_cache!(id: id) | ||
end | ||
end | ||
end | ||
else | ||
puts 'Nothing to do' | ||
end | ||
end | ||
end | ||
end |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
class ReadysetCaches < Readyset::Caches | ||
<% queries.each do |query| -%> | ||
cache id: <%= query.id.dump %>, always: <%= query.always %> do | ||
<<~SQL | ||
<%= query.text.gsub("\n", "\n ") %> | ||
SQL | ||
end | ||
|
||
<%- end -%> | ||
end | ||
|
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
RSpec.describe Readyset::Caches do | ||
describe '.cache' do | ||
after(:each) do | ||
Readyset::Caches.instance_variable_set(:@caches, nil) | ||
end | ||
|
||
it 'adds a cache with the given attributes to the @caches ivar' do | ||
query = build(:cached_query, always: true, count: nil, name: nil) | ||
|
||
Readyset::Caches.cache(always: true, id: query.id) { query.text } | ||
|
||
caches = Readyset::Caches.instance_variable_get(:@caches) | ||
expect(caches.size).to eq(1) | ||
expect(caches.first).to eq(query) | ||
end | ||
|
||
context 'when no always parameter is passed' do | ||
it 'defaults the always parameter to false' do | ||
query = build(:cached_query, count: nil, name: nil) | ||
|
||
Readyset::Caches.cache(id: query.id) { query.text } | ||
|
||
always = Readyset::Caches.instance_variable_get(:@caches).first.always | ||
expect(always).to eq(false) | ||
end | ||
end | ||
end | ||
|
||
describe '.caches' do | ||
it 'returns the caches stored in the @caches ivar' do | ||
query = build(:cached_query, count: nil, name: nil) | ||
Readyset::Caches.cache(always: query.always, id: query.id) { query.text } | ||
|
||
result = Readyset::Caches.caches | ||
|
||
expect(result.size).to eq(1) | ||
expect(result.first).to eq(query) | ||
end | ||
end | ||
end |
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
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
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
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 | ||
describe 'dump' do | ||
it 'dumps the current set of caches to a migration file' do | ||
# Setup | ||
allow(Readyset::Query::CachedQuery).to receive(:all). | ||
and_return([build(:cached_query), build(:cached_query_2)]) | ||
|
||
# Execute | ||
Rake::Task['readyset:caches:dump'].execute | ||
|
||
# Verify | ||
load './spec/internal/db/readyset_caches.rb' | ||
subclasses = Readyset::Caches.subclasses | ||
expect(subclasses.size).to eq(1) | ||
|
||
caches = subclasses.first.caches | ||
expect(caches.size).to eq(2) | ||
expect(caches).to include(build(:cached_query, count: nil, name: nil)) | ||
expect(caches).to include(build(:cached_query_2, count: nil, name: nil)) | ||
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 | ||
end | ||
end |