Skip to content

Commit e23a0b2

Browse files
committed
fix(test): update template creator
It was using old and invalid SQL statements. I took that opportunity to also change a bit its behaviour: - Remove the related rake task and automatically cache the schema if non-existent. - Cache every databases used in tests. - Ensure that cache name is based on schema files.
1 parent 6a526ad commit e23a0b2

File tree

4 files changed

+135
-106
lines changed

4 files changed

+135
-106
lines changed

CONTRIBUTING.md

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -61,23 +61,16 @@ Only do it if you know the schema was left in a correct state.
6161

6262
### Run Tests from a Backup
6363

64-
Loading the full test schema every time a test runs can take a while, so for cases where loading the schema sequentially is unimportant, it is possible to use a backup to set up the database. This is significantly faster than the standard method and is provided to run individual tests faster, but should not be used to validate a build.
65-
66-
First create the template database.
67-
68-
```bash
69-
bundle exec rake db:create_test_template
70-
```
71-
72-
This will create a template database for the current version (ex. `activerecord_test_template611` for version 6.1.1) and create a `BACKUP` in the `nodelocal://self/activerecord-crdb-adapter/#{activerecord_version}` directory.
73-
74-
To load from the template, use the `COCKROACH_LOAD_FROM_TEMPLATE` flag.
75-
76-
```bash
77-
COCKROACH_LOAD_FROM_TEMPLATE=1 TEST_FILES="test/cases/adapters/postgresql/ddl_test.rb" bundle exec rake test
78-
```
79-
80-
And the `activerecord_unittest` database will use the `RESTORE` command to load the schema from the template database.
64+
Loading the full test schema every time a test runs can take
65+
a while, so for cases where loading the schema sequentially
66+
is unimportant, it is possible to use a backup to set up the
67+
database. This is significantly faster than the standard
68+
method and is provided to run individual tests faster, but
69+
should not be used to validate a build.
70+
71+
To do so, just set the env variable `COCKROACH_LOAD_FROM_TEMPLATE`.
72+
First run will generate and cache a template, latter runs will use
73+
it.
8174

8275
# Improvements
8376

Rakefile

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,9 @@ require "bundler/gem_tasks"
22
require "rake/testtask"
33
require_relative 'test/support/paths_cockroachdb'
44
require_relative 'test/support/rake_helpers'
5-
require_relative 'test/support/template_creator'
65

76
task default: [:test]
87

9-
namespace :db do
10-
task "create_test_template" do
11-
ENV['DEBUG_COCKROACHDB_ADAPTER'] = "1"
12-
ENV['COCKROACH_SKIP_LOAD_SCHEMA'] = "1"
13-
14-
TemplateCreator.connect
15-
require_relative 'test/cases/helper'
16-
17-
# TODO: look into this more, but for some reason the blob alias
18-
# is not defined while running this task.
19-
ActiveRecord::ConnectionAdapters::CockroachDB::TableDefinition.class_eval do
20-
alias :blob :binary
21-
end
22-
23-
TemplateCreator.create_test_template
24-
end
25-
end
26-
278
Rake::TestTask.new do |t|
289
t.libs = ARTest::CockroachDB.test_load_paths
2910
t.test_files = RakeHelpers.test_files

test/cases/helper_cockroachdb.rb

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,57 @@ module ExcludeMessage
2222
require "support/load_schema_helper"
2323

2424
module LoadSchemaHelperExt
25+
# Load the CockroachDB specific schema. It replaces ActiveRecord's PostgreSQL
26+
# specific schema.
27+
def load_cockroachdb_specific_schema
28+
# silence verbose schema loading
29+
shh do
30+
load "schema/cockroachdb_specific_schema.rb"
31+
32+
ActiveRecord::FixtureSet.reset_cache
33+
end
34+
end
35+
36+
if ENV['COCKROACH_LOAD_FROM_TEMPLATE'].nil? && ENV['COCKROACH_SKIP_LOAD_SCHEMA'].nil?
37+
load_cockroachdb_specific_schema
38+
end
39+
2540
def load_schema
26-
return if ENV['COCKROACH_LOAD_FROM_TEMPLATE']
2741
return if ENV['COCKROACH_SKIP_LOAD_SCHEMA']
42+
unless ENV['COCKROACH_LOAD_FROM_TEMPLATE']
43+
print "Loading schema..."
44+
t0 = Time.now
45+
super
46+
puts format(" %.2fs", Time.now - t0)
47+
end
48+
49+
require 'support/template_creator'
50+
51+
if TemplateCreator.template_exists?
52+
print "Loading schema from template..."
53+
else
54+
print "Generating and caching template schema..."
55+
end
56+
57+
t0 = Time.now
58+
59+
TemplateCreator.load_from_template do
60+
super
61+
load_cockroachdb_specific_schema
62+
end
2863

29-
super
64+
puts format(" %.2fs", Time.now - t0)
65+
66+
# reconnect to activerecord_unittest
67+
shh { ARTest.connect }
68+
end
69+
70+
private def shh
71+
original_stdout = $stdout
72+
$stdout = StringIO.new
73+
yield
74+
ensure
75+
$stdout = original_stdout
3076
end
3177
end
3278
LoadSchemaHelper.prepend(LoadSchemaHelperExt)
@@ -41,34 +87,6 @@ def load_schema
4187
# Allow exclusion of tests by name using #exclude_from_transactional_tests(test_name)
4288
ActiveRecord::TestCase.prepend(ExcludeFromTransactionalTests)
4389

44-
# Load the CockroachDB specific schema. It replaces ActiveRecord's PostgreSQL
45-
# specific schema.
46-
def load_cockroachdb_specific_schema
47-
# silence verbose schema loading
48-
original_stdout = $stdout
49-
$stdout = StringIO.new
50-
51-
load "schema/cockroachdb_specific_schema.rb"
52-
53-
ActiveRecord::FixtureSet.reset_cache
54-
ensure
55-
$stdout = original_stdout
56-
end
57-
58-
if ENV['COCKROACH_LOAD_FROM_TEMPLATE'].nil? && ENV['COCKROACH_SKIP_LOAD_SCHEMA'].nil?
59-
load_cockroachdb_specific_schema
60-
elsif ENV['COCKROACH_LOAD_FROM_TEMPLATE']
61-
require 'support/template_creator'
62-
63-
p "loading schema from template"
64-
65-
# load from template
66-
TemplateCreator.restore_from_template
67-
68-
# reconnect to activerecord_unittest
69-
ARTest.connect
70-
end
71-
7290
require 'timeout'
7391

7492
module TestTimeoutHelper

test/support/template_creator.rb

Lines changed: 77 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# frozen_string_literal: true
22

3-
require 'active_record'
3+
require "activerecord-cockroachdb-adapter"
44
require_relative 'paths_cockroachdb'
5+
require 'support/config' # ARTest.config
6+
require 'support/connection' # ARTest.connect
57

68
module TemplateCreator
7-
# extend self
8-
99
DEFAULT_CONNECTION_HASH = {
1010
adapter: 'cockroachdb',
1111
database: 'defaultdb',
@@ -14,37 +14,60 @@ module TemplateCreator
1414
host: 'localhost'
1515
}.freeze
1616

17-
BACKUP_DIR = "nodelocal://self/activerecord-crdb-adapter"
17+
BACKUP_DIR = "userfile://defaultdb.public/activerecord-crdb-adapter"
1818

1919
module_function
2020

21-
def ar_version
22-
ActiveRecord.version.version.gsub('.','')
21+
def template_version
22+
ar_version = ActiveRecord.version.version.gsub('.','_')
23+
main_schema_digest = Digest::MD5.file(SCHEMA_ROOT + "/schema.rb").hexdigest
24+
crdb_schema_digest = Digest::MD5.file("#{__dir__}/../schema/cockroachdb_specific_schema.rb").hexdigest
25+
"#{ar_version}_#{main_schema_digest}_#{crdb_schema_digest}"
2326
end
2427

2528
def version_backup_path
26-
BACKUP_DIR + "/#{ar_version}"
29+
BACKUP_DIR + "/#{template_version}"
30+
end
31+
32+
def template_db_name(db_name)
33+
"#{db_name}__template__#{template_version}"
34+
end
35+
36+
# NOTE: this verification that databases exist may not be enough if a failure
37+
# occurs during the backup process. However, a developer should see that
38+
# failure happening, and in new envs we always run at least a first time
39+
# the backup process.
40+
def template_exists?
41+
databases.all? { template_db_exists?(_1) }
42+
end
43+
44+
def connect
45+
ActiveRecord::Base.establish_connection(DEFAULT_CONNECTION_HASH)
2746
end
2847

29-
def template_db_name
30-
"activerecord_unittest_template#{ar_version}"
48+
def databases
49+
@databases ||=ARTest.config.dig("connections", "cockroachdb").map { |_, value| value["database"] }.uniq
3150
end
3251

33-
def connect(connection_hash=nil)
34-
connection_hash = DEFAULT_CONNECTION_HASH if connection_hash.nil?
35-
ActiveRecord::Base.establish_connection(connection_hash)
52+
def with_template_db_names
53+
old_crdb = ARTest.config["connections"]["cockroachdb"].dup
54+
new_crdb = old_crdb.transform_values { |value| value.merge("database" => template_db_name(value["database"])) }
55+
ARTest.config["connections"]["cockroachdb"] = new_crdb
56+
yield
57+
ensure
58+
ARTest.config["connections"]["cockroachdb"] = old_crdb
3659
end
3760

38-
def template_db_exists?
39-
ActiveRecord::Base.lease_connection.select_value("SELECT 1 FROM pg_database WHERE datname='#{template_db_name}'") == 1
61+
def template_db_exists?(db_name)
62+
ActiveRecord::Base.lease_connection.select_value("SELECT 1 FROM pg_database WHERE datname='#{template_db_name(db_name)}'") == 1
4063
end
4164

42-
def drop_template_db
43-
ActiveRecord::Base.lease_connection.execute("DROP DATABASE #{template_db_name}")
65+
def drop_template_db(db_name)
66+
ActiveRecord::Base.lease_connection.execute("DROP DATABASE #{template_db_name(db_name)} CASCADE")
4467
end
4568

46-
def create_template_db
47-
ActiveRecord::Base.lease_connection.execute("CREATE DATABASE #{template_db_name}")
69+
def create_template_db(db_name)
70+
ActiveRecord::Base.lease_connection.execute("CREATE DATABASE #{template_db_name(db_name)}")
4871
end
4972

5073
def load_schema
@@ -53,36 +76,50 @@ def load_schema
5376
load 'test/schema/cockroachdb_specific_schema.rb'
5477
end
5578

56-
def create_test_template
79+
def create_test_template(&block)
5780
connect
58-
raise "#{template_db_name} already exists. If you do not have a backup created, please drop the database and run again." if template_db_exists?
59-
60-
create_template_db
61-
62-
# switch connection to template db
63-
conn = DEFAULT_CONNECTION_HASH.dup
64-
conn['database'] = template_db_name
65-
connect(conn)
81+
databases.each do |db_name|
82+
drop_template_db(db_name) if template_db_exists?(db_name)
83+
create_template_db(db_name)
84+
end
6685

67-
load_schema
86+
with_template_db_names do
87+
shh { ARTest.connect }
88+
block.call
89+
end
6890

69-
# create BACKUP to restore from
70-
ActiveRecord::Base.lease_connection.execute("BACKUP DATABASE #{template_db_name} TO '#{version_backup_path}'")
91+
connect
92+
ActiveRecord::Base.lease_connection.execute(<<~SQL)
93+
BACKUP DATABASE #{databases.map { |db| template_db_name(db) }.join(', ')}
94+
INTO '#{version_backup_path}'
95+
SQL
7196
end
7297

73-
def restore_from_template
98+
def load_from_template(&block)
7499
connect
75-
raise "The TemplateDB does not exist. Run 'rake db:create_test_template' first." unless template_db_exists?
76-
77-
begin
78-
ActiveRecord::Base.lease_connection.execute("DROP DATABASE activerecord_unittest")
79-
rescue ActiveRecord::StatementInvalid => e
80-
unless e.cause.class == PG::InvalidCatalogName
81-
raise e
100+
create_test_template(&block) unless template_exists?
101+
databases.each do |db_name|
102+
begin
103+
ActiveRecord::Base.lease_connection.execute("DROP DATABASE #{db_name}")
104+
rescue ActiveRecord::StatementInvalid => e
105+
unless e.cause.class == PG::InvalidCatalogName
106+
raise e
107+
end
82108
end
109+
ActiveRecord::Base.lease_connection.execute("CREATE DATABASE #{db_name}")
110+
ActiveRecord::Base.lease_connection.execute(<<~SQL)
111+
RESTORE #{template_db_name(db_name)}.*
112+
FROM LATEST IN '#{version_backup_path}'
113+
WITH into_db = '#{db_name}'
114+
SQL
83115
end
84-
ActiveRecord::Base.lease_connection.execute("CREATE DATABASE activerecord_unittest")
116+
end
85117

86-
ActiveRecord::Base.lease_connection.execute("RESTORE #{template_db_name}.* FROM '#{version_backup_path}' WITH into_db = 'activerecord_unittest'")
118+
private def shh
119+
original_stdout = $stdout
120+
$stdout = StringIO.new
121+
yield
122+
ensure
123+
$stdout = original_stdout
87124
end
88125
end

0 commit comments

Comments
 (0)