Миграция данных с помощью space:upgrade()
В этом руководстве описана миграция данных в Tarantool DB с помощью метода space:upgrade().
space:upgrade() позволяет вносить несовместимые изменения в формат спейса, например изменить название спейса или удалить его.
Подробнее о миграции можно прочитать в разделе Миграция данных.
Руководство включает следующие шаги:
- Пререквизиты
- Схема данных
- Запуск стенда
- Подключение к кластеру и загрузка данных
- Метод space:upgrade()
- Определение кода миграций
- Запуск миграции
- Остановка стенда
Для выполнения примера требуются:
-
установленный Docker-образ Tarantool DB;
-
приложение Docker compose;
-
утилита TT CLI;
-
исходные файлы примера
space_upgrade.
В примере используется база данных для системы управления проектами, которая состоит из трех спейсов: projects (проекты), tasks (задачи),
users (пользователи). Изначально схема этой базы данных выглядит так:
После прохождения руководства схема данных будет изменена следующим образом:
- добавлено поле
assigned_manager_idв спейсprojects; - изменен тип поля
statusв спейсеtasksсоstringнаnumber; - добавлено поле
due_date(datetime)в спейсtasks; - изменено название поля
emailнаcontactв спейсеusers; - добавлено поле
roleв спейсusers.
После изменений схема данных будет выглядеть так:
Для запуска и настройки кластера используются файлы из папки space_upgrade:
docker-compose.yml— описание узлов кластера;installer/topology.json— описание топологии кластера.
Перейдите в директорию примера space_upgrade:
cd ./docs/docs/ru/assets/examples/space_upgrade/
Запустите стенд:
docker compose up -d --force-recreate
Команда развернет кластер с первоначальной схемой данных:
Подключитесь к роутеру с помощью команды tt connect.
Команда открывает интерактивную консоль Tarantool, позволяющую работать с базой данных:
tt connect admin:secret-cluster-cookie@localhost:3300
Загрузить данные в кластер можно с помощью функции __fill_data:
box.schema.func.call('__fill_data')
Исходный код функции приведен в файле 001_test.lua в директории ./bootstrap/migrations/source/ примера migrations_space_upgrade.
Дождитесь окончания загрузки данных, это может занять до трех минут. В результате на каждом хранилище будет занято по 164 MB данных. Процесс заполнения кластера можно отследить в логах:
space_upgrade-tarantool-router-1 | 2024-02-26 05:46:50.947 [12] main/189/main/tarantool I> start to fill_dataspace_upgrade-tarantool-router-1 | 2024-02-26 05:46:50.947 [12] main/189/main/tarantool I> send batch 1...space_upgrade-tarantool-router-1 | 2024-02-26 05:49:01.559 [12] main/189/main/tarantool I> send batch 700space_upgrade-tarantool-router-1 | 2024-02-26 05:49:01.795 [12] main/189/main/tarantool I> data filled
space:upgrade() принимает следующие аргументы:
format: новый формат спейса. Он может конфликтовать с предыдущим, но индексируемые поля должны быть неизменными. Данные, записываемые в спейс, должны соответствовать новому формату во время миграции.func: название функции выполнения миграций. Функция должна быть персистентной. Она принимает исходный кортеж, а возвращает измененный кортеж. Функция должна быть идемпотентной, чтобы избежать ошибки миграции или получения некорректных данных при чтении данных из спейса во время миграции. а из этого следует, чтоfuncможет быть применена кtupleнесколько раз.mode: режим работыspace:upgrade. Возможные значения:dryrun— проверка корректности миграции. Функцияfuncвыполняется на каждом кортеже, но данные не меняются;upgrade— обновление данных;dryrun+upgrade— запуск проверки миграции, после которой при отсутствии ошибок выполняетсяupgrade.
is_async- булевый флаг неблокируемого выполненияspace:upgrade.
space:upgrade возвращает объект future. По нему можно узнать статус миграции (future:info), отменить миграцию (future:cancel), или дождаться конца миграции (future:wait).
Подробная информация о методе space:upgrade приведена в документации Tarantool Enterprise.
Исходный код миграции приведен в файле 002_test.lua в корневой директории примера migrations_space_upgrade.
В спейсе projects нужно добавить assigned_manager_id между полями name и description.
При работе с кортежами используется встроенная библиотека box.tuple.
Определите функцию для изменения кортежей:
-- функция для преобразования кортежейbox.schema.func.create('__migrator_projects_002', { -- давайте функции название с номером миграцииlanguage = 'lua', -- функция на Luais_deterministic = true, -- функция детерминированнаяbody = [[function(t)if #t == 4 thenreturn t:update({{'!', 4, box.NULL}})endreturn tend]]})
Теперь обновите формат спейса с помощью space:upgrade().
space:upgrade() возвращает объект future.
Запишите этот объект в переменную projects_migration, чтобы отслеживать прогресс миграции:
local projects_migration = box.space.projects:upgrade({func = '__migrator_projects_002',format = {{ name = 'project_id', type = 'uuid' },{ name = 'bucket_id', type = 'unsigned' },{ name = 'name', type = 'string' },{ name = 'assigned_manager_id', type = 'uuid', is_nullable = true },{ name = 'description', type = 'string', is_nullable = true },},mode = 'dryrun+upgrade',is_async = true,})
Сохраните объект projects_migration в глобальную переменную, чтобы иметь к ней доступ из консоли tt:
rawset(_G, '__projects_migration', projects_migration)
В спейс tasks нужно изменить тип поля status с number на string, а также добавить в конец поля due_date(datetime).
Определите функцию для изменения кортежей:
box.schema.func.create('__migrator_tasks_002', {language = 'lua',is_deterministic = true,body = [[function(t)-- задана дата по умолчанию для due_datelocal datetime = require('datetime')local due_date = datetime.new({year=2999, month=12, day=31})-- проверяем, что полей 7. Если их 7, это означает, что поле due_date добавлено не былоif #t == 7 then-- для смены типа поля необходимо, его удалить и добавить новое-- функция `tuple:transform` подходит для этого.-- https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_tuple/transform/-- t:transform(5, 1, 0) удалит одно поле начиная с пятого(status) и добавит вместо него число 0-- update({{"!", 8, due_date}}) добавит поле в 8-ю позицию, т.е. в конец и присвоит полю значение due_datereturn t:transform(5, 1, 0):update({{"!", 8, due_date}})endreturn tend]],})
Обновите формат спейса.
Запишите объект future в переменную tasks_migration:
local tasks_migration = box.space.tasks:upgrade({func = '__migrator_tasks_002',format = {{ name = 'task_id', type = 'uuid' },{ name = 'bucket_id', type = 'unsigned' },{ name = 'name', type = 'string' },{ name = 'description', type = 'string', is_nullable = true },-- изменили тип поля status{ name = 'status', type = 'number' },{ name = 'project_id', type = 'uuid' },{ name = 'assigned_user_id', type = 'uuid', is_nullable = true },-- добавили новое поле due_date{ name = 'due_date', type = 'datetime' },},mode = 'dryrun+upgrade',is_async = true,})
Сохраните объект tasks_migration в глобальную переменную, чтобы иметь к ней доступ из консоли tt:
rawset(_G, '__tasks_migration', tasks_migration)
В спейсе users нужно изменить название поля email на contact, а также добавить новое поле role.
box.schema.func.create('__migrator_users_002', {language = 'lua',is_deterministic = true,body = [[function(t)-- проверяем, что поле `role` еще не добавленоif #t == 4 then-- добавлем новое поле на 4-ю позицию, между `name` и `contact`return t:update({{'!', 4, 'not set'}})endreturn tend]],})
Обновите формат спейса.
Запишите объект future в переменную users_migration:
local users_migration = box.space.users:upgrade({func = '__migrator_users_002',format = {{ name = 'user_id', type = 'uuid' },{ name = 'bucket_id', type = 'unsigned' },{ name = 'name', type = 'string' },-- новое поле `role`{ name = 'role', type = 'string' },-- изменяем название поля на `contact`{ name = 'contact', type = 'string' },},mode = 'dryrun+upgrade',is_async = true,})
Сохраните объект users_migration в глобальную переменную, чтобы иметь к ней доступ из консоли tt:
rawset(_G, '__users_migration', users_migration)
Загрузите файл с миграцией в конфигурацию кластера. Подробнее о загрузке миграции в конфигурацию рассказано в разделе Способы выполнения миграции.
curl -v --raw 'http://localhost:8081/admin/api' -X POST --data '{"query":"mutation($sections: [ConfigSectionInput!]) {cluster {config(sections: $sections) {filenamecontent}}}","variables": {"sections": [{"filename":"migrations/source/002_test.lua","content":"'"$(cat 002_test.lua | sed 's/"/\\"/g' )"'"}]}}'
Запустите миграцию:
curl -X POST localhost:8081/migrations/up
Ответ выглядит так:
`{"applied":["002_test.lua"]}
После подключитесь к узлу хранилища, используя команду tt connect:
tt connect admin:secret-cluster-cookie@localhost:3301
Чтобы просмотреть статусы миграции спейса, вызовите соответствующую глобальную переменную, заданную в конфигурации:
localhost:3301> __projects_migration- owner: baf5b6ba-d594-4b80-856e-02e1f05de5c7func: __migrator_projects_002progress: 74%status: inprogressdryrun: truelocalhost:3301> __tasks_migration- status: inprogressprogress: 1%owner: baf5b6ba-d594-4b80-856e-02e1f05de5c7func: __migrator_tasks_002localhost:3301> __users_migration- status: inprogressprogress: 32%owner: baf5b6ba-d594-4b80-856e-02e1f05de5c7func: __migrator_users_002...
В space:upgrade в режиме dryrun+upgrade сначала выполняется проверка (dryrun) данных без их изменений.
Если ошибок нет, начинается обновление данных.
На этапе проверки флаг dryrun принимает значение true:
localhost:3301> __projects_migration- owner: baf5b6ba-d594-4b80-856e-02e1f05de5c7func: __migrator_projects_002progress: 74%status: inprogressdryrun: true
При space:upgrade() запросы на чтение данных из мигрируемых спейсов отдают данные в нужном виде.
Проверить это можно, если выполнить код ниже во время миграций, когда в __projects_migration, __tasks_migration, __users_migration отсутствует флаг dryrun: true:
localhost:3301> box.space.users:pairs({require('uuid').new()}, 'GE'):take_n(2):map(function(t) return t:tomap({names_only=true}) end):totable()---- - bucket_id: 12192contact: john.doe863@example.comrole: not setuser_id: f62c5c9a-b739-4a7d-97b9-5798281eff38name: john_doe 863- bucket_id: 11368contact: john.doe854@example.comrole: not setuser_id: f62cc684-fb92-4f7a-a6d4-132d20803bb9name: john_doe 854localhost:3301> box.space.projects:pairs({require('uuid').new()}, 'GE'):take_n(2):map(function(t) return t:tomap({names_only=true}) end):totable()---- - bucket_id: 12580project_id: 45c5ee48-c725-440c-9310-fd014b6ff672assigned_manager_id: nullname: Task Management 74description: Development of a task management system 74- bucket_id: 13977project_id: 45c62b29-49a3-4170-87f5-a5f149b00722assigned_manager_id: nullname: Task Management 652description: Development of a task management system 652localhost:3301> box.space.tasks:pairs({require('uuid').new()}, 'GE'):take_n(2):map(function(t) return t:tomap({names_only=true}) end):totable()---- - bucket_id: 9071project_id: f267b29a-413e-4b84-bc23-35a6b6ff101ctask_id: 5c0c561c-3c17-4bcf-b2c2-6918708d2213assigned_user_id: c12d08a0-7527-45fd-8493-bf8fc2a1b50bstatus: 0due_date: 2999-12-31T00:00:00Zname: Create New Logodescription: Design a new logo for the website.- bucket_id: 4293project_id: 9e165c07-c6e5-4495-a66e-6e70a99a3ce1task_id: 5c0c5e39-cbc4-40d7-8c64-435a6a19f3edassigned_user_id: 3517d823-1879-497a-8005-ac4c16d94f5bstatus: 0due_date: 2999-12-31T00:00:00Zname: Create New Logodescription: Design a new logo for the website.
После окончания миграций содержимое глобальных переменных с объектом future выглядит так:
localhost:3301> __projects_migration---- status: done...localhost:3301> __tasks_migration---- status: done...localhost:3301> __users_migration---- status: done...
Окончание миграции данных в логах выглядит так:
space_upgrade-tarantool-storage3-1 | 2024-02-26 09:13:43.048 [12] main/172/space_upgrade_516 I> space upgrade completedspace_upgrade-tarantool-storage3-1 | 2024-02-26 09:13:43.133 [12] main/173/space_upgrade_513 I> space upgrade completedspace_upgrade-tarantool-storage3-1 | 2024-02-26 09:13:43.163 [12] main/174/space_upgrade_515 I> space upgrade completed
Проверить выполнение миграции и изменение формата можно в веб-интерфейсе во вкладке Space explorer.
Чтобы остановить стенд, выполните следующую команду:
docker compose down