Skip to content

Commit e63c1a6

Browse files
BuonOmorafiss
authored andcommitted
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. - Rely on DefaultDB for db manipulations since the others might not exist.
1 parent 1b17c99 commit e63c1a6

File tree

4 files changed

+135
-117
lines changed

4 files changed

+135
-117
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: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,52 +22,70 @@ 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+
2536
def load_schema
26-
return if ENV['COCKROACH_LOAD_FROM_TEMPLATE']
2737
return if ENV['COCKROACH_SKIP_LOAD_SCHEMA']
38+
return load_from_template { super } if ENV['COCKROACH_LOAD_FROM_TEMPLATE']
2839

40+
print "Loading schema..."
41+
t0 = Time.now
2942
super
43+
load_cockroachdb_specific_schema
44+
puts format(" %.2fs", Time.now - t0)
45+
return
3046
end
31-
end
32-
LoadSchemaHelper.prepend(LoadSchemaHelperExt)
3347

34-
require "activerecord-cockroachdb-adapter"
48+
private def load_from_template(&)
49+
require 'support/template_creator'
3550

36-
# Load ActiveRecord test helper
37-
require "cases/helper"
51+
if TemplateCreator.template_exists?
52+
print "Loading schema from template..."
53+
else
54+
print "Generating and caching template schema..."
55+
end
3856

39-
require "support/exclude_from_transactional_tests"
57+
t0 = Time.now
4058

41-
# Allow exclusion of tests by name using #exclude_from_transactional_tests(test_name)
42-
ActiveRecord::TestCase.prepend(ExcludeFromTransactionalTests)
59+
TemplateCreator.load_from_template do
60+
yield
61+
load_cockroachdb_specific_schema
62+
end
4363

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
64+
puts format(" %.2fs", Time.now - t0)
5065

51-
load "schema/cockroachdb_specific_schema.rb"
66+
# reconnect to activerecord_unittest
67+
shh { ARTest.connect }
68+
end
5269

53-
ActiveRecord::FixtureSet.reset_cache
54-
ensure
55-
$stdout = original_stdout
70+
private def shh
71+
original_stdout = $stdout
72+
$stdout = StringIO.new
73+
yield
74+
ensure
75+
$stdout = original_stdout
76+
end
5677
end
78+
LoadSchemaHelper.prepend(LoadSchemaHelperExt)
5779

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'
80+
require "activerecord-cockroachdb-adapter"
6281

63-
p "loading schema from template"
82+
# Load ActiveRecord test helper
83+
require "cases/helper"
6484

65-
# load from template
66-
TemplateCreator.restore_from_template
85+
require "support/exclude_from_transactional_tests"
6786

68-
# reconnect to activerecord_unittest
69-
ARTest.connect
70-
end
87+
# Allow exclusion of tests by name using #exclude_from_transactional_tests(test_name)
88+
ActiveRecord::TestCase.prepend(ExcludeFromTransactionalTests)
7189

7290
require 'timeout'
7391

test/support/template_creator.rb

Lines changed: 78 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,114 @@
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-
9-
DEFAULT_CONNECTION_HASH = {
10-
adapter: 'cockroachdb',
11-
database: 'defaultdb',
12-
port: 26257,
13-
user: 'root',
14-
host: 'localhost'
15-
}.freeze
9+
class DefaultDB < ActiveRecord::Base
10+
establish_connection(
11+
adapter: 'cockroachdb',
12+
database: 'defaultdb',
13+
port: 26257,
14+
user: 'root',
15+
host: 'localhost'
16+
)
17+
end
1618

17-
BACKUP_DIR = "nodelocal://self/activerecord-crdb-adapter"
19+
# Database created once the backup is finished to make sure we have a
20+
# clean backup to work with. See #template_exists?
21+
EXISTS = "exists"
22+
BACKUP_DIR = "userfile://defaultdb.public/activerecord-crdb-adapter"
1823

1924
module_function
2025

21-
def ar_version
22-
ActiveRecord.version.version.gsub('.','')
26+
def template_version
27+
ar_version = ActiveRecord.version.version.gsub('.','_')
28+
main_schema_digest = Digest::MD5.file(SCHEMA_ROOT + "/schema.rb").hexdigest
29+
crdb_schema_digest = Digest::MD5.file("#{__dir__}/../schema/cockroachdb_specific_schema.rb").hexdigest
30+
"#{ar_version}_#{main_schema_digest}_#{crdb_schema_digest}"
2331
end
2432

2533
def version_backup_path
26-
BACKUP_DIR + "/#{ar_version}"
34+
BACKUP_DIR + "/#{template_version}"
2735
end
2836

29-
def template_db_name
30-
"activerecord_unittest_template#{ar_version}"
37+
def template_db_name(db_name)
38+
"#{db_name}__template__#{template_version}"
3139
end
3240

33-
def connect(connection_hash=nil)
34-
connection_hash = DEFAULT_CONNECTION_HASH if connection_hash.nil?
35-
ActiveRecord::Base.establish_connection(connection_hash)
41+
def template_exists?
42+
template_db_exists?(EXISTS)
3643
end
3744

38-
def template_db_exists?
39-
ActiveRecord::Base.lease_connection.select_value("SELECT 1 FROM pg_database WHERE datname='#{template_db_name}'") == 1
45+
def databases
46+
@databases ||=ARTest.config.dig("connections", "cockroachdb").map { |_, value| value["database"] }.uniq
4047
end
4148

42-
def drop_template_db
43-
ActiveRecord::Base.lease_connection.execute("DROP DATABASE #{template_db_name}")
49+
def with_template_db_names
50+
old_crdb = ARTest.config["connections"]["cockroachdb"]
51+
new_crdb = old_crdb.transform_values { _1.merge("database" => template_db_name(_1["database"])) }
52+
ARTest.config["connections"]["cockroachdb"] = new_crdb
53+
yield
54+
ensure
55+
ARTest.config["connections"]["cockroachdb"] = old_crdb
4456
end
4557

46-
def create_template_db
47-
ActiveRecord::Base.lease_connection.execute("CREATE DATABASE #{template_db_name}")
58+
def template_db_exists?(db_name)
59+
DefaultDB.lease_connection.select_value("SELECT 1 FROM pg_database WHERE datname='#{template_db_name(db_name)}'") == 1
4860
end
4961

50-
def load_schema
51-
p 'loading schema'
52-
load ARTest::CockroachDB.root_activerecord_test + '/schema/schema.rb'
53-
load 'test/schema/cockroachdb_specific_schema.rb'
62+
def drop_template_db(db_name)
63+
DefaultDB.lease_connection.execute("DROP DATABASE #{template_db_name(db_name)} CASCADE")
5464
end
5565

56-
def create_test_template
57-
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
66+
def create_template_db(db_name)
67+
DefaultDB.lease_connection.execute("CREATE DATABASE #{template_db_name(db_name)}")
68+
end
6169

62-
# switch connection to template db
63-
conn = DEFAULT_CONNECTION_HASH.dup
64-
conn['database'] = template_db_name
65-
connect(conn)
70+
def create_test_template(&block)
71+
databases.each do |db_name|
72+
drop_template_db(db_name) if template_db_exists?(db_name)
73+
create_template_db(db_name)
74+
end
6675

67-
load_schema
76+
with_template_db_names do
77+
shh { ARTest.connect }
78+
block.call
79+
end
6880

69-
# create BACKUP to restore from
70-
ActiveRecord::Base.lease_connection.execute("BACKUP DATABASE #{template_db_name} TO '#{version_backup_path}'")
81+
DefaultDB.lease_connection.execute(<<~SQL)
82+
BACKUP DATABASE #{databases.map { |db| template_db_name(db) }.join(', ')}
83+
INTO '#{version_backup_path}'
84+
SQL
85+
create_template_db(EXISTS)
7186
end
7287

73-
def restore_from_template
74-
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
88+
def load_from_template(&block)
89+
create_test_template(&block) unless template_exists?
90+
databases.each do |db_name|
91+
begin
92+
DefaultDB.lease_connection.execute("DROP DATABASE #{db_name}")
93+
rescue ActiveRecord::StatementInvalid => e
94+
unless e.cause.class == PG::InvalidCatalogName
95+
raise e
96+
end
8297
end
98+
DefaultDB.lease_connection.execute("CREATE DATABASE #{db_name}")
99+
DefaultDB.lease_connection.execute(<<~SQL)
100+
RESTORE #{template_db_name(db_name)}.*
101+
FROM LATEST IN '#{version_backup_path}'
102+
WITH into_db = '#{db_name}'
103+
SQL
83104
end
84-
ActiveRecord::Base.lease_connection.execute("CREATE DATABASE activerecord_unittest")
105+
end
85106

86-
ActiveRecord::Base.lease_connection.execute("RESTORE #{template_db_name}.* FROM '#{version_backup_path}' WITH into_db = 'activerecord_unittest'")
107+
private def shh
108+
original_stdout = $stdout
109+
$stdout = StringIO.new
110+
yield
111+
ensure
112+
$stdout = original_stdout
87113
end
88114
end

0 commit comments

Comments
 (0)