Я застрял в использовании TimescaleDB в Rails - все работает нормально, но в моем наборе тестов я не могу вставить никаких данных.
Что я пробовал
A) Используйте дамп схемы SQL
Это вызывает исходное сообщение об ошибке, которое я видел. Он действительно создает части схемы для TimescaleDB, но не всю ее. У меня есть гипертаблица, но она не работает должным образом
Б) Используйте дамп схемы Ruby
Это позволяет мне вставлять в свою таблицу, но это совсем не гипертаблица - синтаксис ruby теряет все, что связано с TimescaleDB и гипертаблицами.
C) Перенести тестовую базу данных напрямую
Я попытался избежать дампа и загрузки schema.structure следующим образом:
$ rails db:drop
Dropped database 'my_app_development'
Dropped database 'my_app_test'
$ RAILS_ENV=test rails db:create
Created database 'my_app_test'
$ RAILS_ENV=test rails db:migrate
== 20200517164444 EnableTimescaledbExtension: migrating =======================
-- enable_extension("timescaledb")
WARNING:
WELCOME TO
_____ _ _ ____________
|_ _(_) | | | _ \ ___ \
| | _ _ __ ___ ___ ___ ___ __ _| | ___| | | | |_/ /
| | | | _ ` _ \ / _ \/ __|/ __/ _` | |/ _ \ | | | ___ \
| | | | | | | | | __/\__ \ (_| (_| | | __/ |/ /| |_/ /
|_| |_|_| |_| |_|\___||___/\___\__,_|_|\___|___/ \____/
Running version 1.7.0
For more information on TimescaleDB, please visit the following links:
1. Getting started: https://docs.timescale.com/getting-started
2. API reference documentation: https://docs.timescale.com/api
3. How TimescaleDB is designed: https://docs.timescale.com/introduction/architecture
Note: TimescaleDB collects anonymous reports to better understand and assist our users.
For more information and how to disable, please see our docs https://docs.timescaledb.com/using-timescaledb/telemetry.
-> 0.2315s
== 20200517164444 EnableTimescaledbExtension: migrated (0.2316s) ==============
== 20200517165027 CreateAccounts: migrating ===================================
-- create_table(:accounts)
-> 0.0095s
== 20200517165027 CreateAccounts: migrated (0.0095s) ==========================
== 20200517165103 CreateMetrics: migrating ====================================
-- create_table(:metrics)
-> 0.0116s
== 20200517165103 CreateMetrics: migrated (0.0117s) ===========================
== 20200517170842 CreateEvents: migrating =====================================
-- create_table(:events)
-> 0.0072s
-- remove_column(:events, :id)
-> 0.0020s
-- execute("SELECT create_hypertable('events', 'time');\n")
-> 0.0047s
== 20200517170842 CreateEvents: migrated (0.0142s) ============================
pg_dump: warning: there are circular foreign-key constraints on this table:
pg_dump: hypertable
pg_dump: You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints.
pg_dump: Consider using a full dump instead of a --data-only dump to avoid this problem.
pg_dump: warning: there are circular foreign-key constraints on this table:
pg_dump: chunk
pg_dump: You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints.
pg_dump: Consider using a full dump instead of a --data-only dump to avoid this problem.
Но при запуске набора тестов это то же самое, что и попытка A.
Выполнение тестов после того, как на самом деле напечатает это сообщение несколько раз, что заставляет меня думать, что Rails автоматически магическим образом снова использует structure.sql
для воссоздания тестовой БД:
psql:/home/axel/src/my_app/db/structure.sql:16: WARNING:
WELCOME TO
_____ _ _ ____________
|_ _(_) | | | _ \ ___ \
| | _ _ __ ___ ___ ___ ___ __ _| | ___| | | | |_/ /
| | | | _ ` _ \ / _ \/ __|/ __/ _` | |/ _ \ | | | ___ \
| | | | | | | | | __/\__ \ (_| (_| | | __/ |/ /| |_/ /
|_| |_|_| |_| |_|\___||___/\___\__,_|_|\___|___/ \____/
Running version 1.7.0
For more information on TimescaleDB, please visit the following links:
1. Getting started: https://docs.timescale.com/getting-started
2. API reference documentation: https://docs.timescale.com/api
3. How TimescaleDB is designed: https://docs.timescale.com/introduction/architecture
Note: TimescaleDB collects anonymous reports to better understand and assist our users.
For more information and how to disable, please see our docs https://docs.timescaledb.com/using-timescaledb/telemetry.
Сообщение об ошибке
$ rails test
Running via Spring preloader in process 107937
Run options: --seed 29840
# Running:
E
Error:
Api::EventsControllerTest#test_POST_event_data_-_new_metric:
DRb::DRbRemoteError: PG::FeatureNotSupported: ERROR: invalid INSERT on the root table of hypertable "events"
HINT: Make sure the TimescaleDB extension has been preloaded.
(ActiveRecord::StatementInvalid)
app/controllers/api/events_controller.rb:5:in `create'
test/controllers/api/events_controller_test.rb:9:in `block in <class:EventsControllerTest>'
rails test test/controllers/api/events_controller_test.rb:8
Finished in 0.215286s, 4.6450 runs/s, 0.0000 assertions/s.
1 runs, 0 assertions, 0 failures, 1 errors, 0 skips
У меня такое чувство, что это связано с тем, как Rails создает тестовую базу данных с использованием schema.rb
(для config.active_record.schema_format = :ruby
по умолчанию) или structure.sql
(для config.active_record.schema_format = :sql
. Я уже пробовал оба, настройки структуры Ruby и SQL, и ни один из них не работает - база данных разработки переносится правильно, но тестовая БД настроена неправильно.
В двух базах данных ниже (для разработки и тестирования) мы видим единственное различие в том, что тестовая БД отсутствует: Child tables: _timescaledb_internal._hyper_1_1_chunk
БД разработки
$ psql -d my_app_development
psql (12.2)
Type "help" for help.
my_app_development=# SHOW shared_preload_libraries;
shared_preload_libraries
--------------------------
timescaledb
(1 row)
my_app_development=# insert into events (metric_id, time, value) VALUES (1, NOW(), 22);
INSERT 0 1
my_app_development=# \d+ events
Table "public.events"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+-----------------------------+-----------+----------+---------+---------+--------------+-------------
metric_id | bigint | | | | plain | |
time | timestamp without time zone | | not null | | plain | |
value | numeric | | | | main | |
Indexes:
"events_time_idx" btree ("time" DESC)
Triggers:
ts_insert_blocker BEFORE INSERT ON events FOR EACH ROW EXECUTE FUNCTION _timescaledb_internal.insert_blocker()
Child tables: _timescaledb_internal._hyper_1_1_chunk
Access method: heap
Тестовая БД
$ psql -d my_app_test
psql (12.2)
Type "help" for help.
my_app_test=# SHOW shared_preload_libraries;
shared_preload_libraries
--------------------------
timescaledb
(1 row)
my_app_test=# insert into events (metric_id, time, value) VALUES (1, NOW(), 22);
ERROR: invalid INSERT on the root table of hypertable "events"
HINT: Make sure the TimescaleDB extension has been preloaded.
my_app_test=# \d+ events
Table "public.events"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+-----------------------------+-----------+----------+---------+---------+--------------+-------------
metric_id | bigint | | | | plain | |
time | timestamp without time zone | | not null | | plain | |
value | numeric | | | | main | |
Indexes:
"events_time_idx" btree ("time" DESC)
Triggers:
ts_insert_blocker BEFORE INSERT ON events FOR EACH ROW EXECUTE FUNCTION _timescaledb_internal.insert_blocker()
Access method: heap
ActiveRecord со схемой SQL
CREATE EXTENSION IF NOT EXISTS timescaledb WITH SCHEMA public;
SET default_tablespace = '';
SET default_table_access_method = heap;
CREATE TABLE public.events (
metric_id bigint,
"time" timestamp without time zone NOT NULL,
value numeric
);
CREATE INDEX events_time_idx ON public.events USING btree ("time" DESC);
CREATE TRIGGER ts_insert_blocker BEFORE INSERT ON public.events FOR EACH ROW EXECUTE FUNCTION _timescaledb_internal.insert_blocker();
ActiveRecord со схемой Ruby
ActiveRecord::Schema.define(version: 2020_05test/test_helper.rb
170842) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
enable_extension "timescaledb"
create_table "events", id: false, force: :cascade do |t|
t.bigint "metric_id"
t.datetime "time", null: false
t.decimal "value"
t.index ["time"], name: "events_time_idx", order: :desc
end
end
Примечание: это теряет триггер ts_insert_blocker
и позволяет мне вставить в таблицу events
, но это больше не гипертаблица:
my_app_test=# \d+ events
Table "public.events"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------+-----------------------------+-----------+----------+---------+---------+--------------+-------------
metric_id | bigint | | | | plain | |
time | timestamp without time zone | | not null | | plain | |
value | numeric | | | | main | |
Indexes:
"events_time_idx" btree ("time" DESC)
Access method: heap
Дополнительная информация
Связанный вопрос: Запуск RSpec набор тестов для базы данных TimescaleDB с Rails 4.2 - предложения не сработали для меня, и нет принятого ответа.
Информация о версии:
- Рельсы 6.0.3
- Postgres 12.2
- Шкала времениDB 1.7.0
Редактировать 1
Я добавил в свой test/test_helper.rb
следующее, похожее на обходной путь, упомянутый @cstabru
def execute_create_hypertable(sql)
ActiveRecord::Base.connection.execute(sql)
rescue ActiveRecord::StatementInvalid => e
raise e unless e.message.include? 'is already a hypertable'
end
execute_create_hypertable <<~SQL
SELECT create_hypertable('events', 'time');
SQL
Но, может быть, мы можем использовать что-то вроде SELECT create_hypertable('hypertable_name', 'time_field', if_not_exists => TRUE
в инициализаторе вместо создания гипертаблиц при миграции БД?