{"id":20564389,"url":"https://github.com/tarantool/migrations","last_synced_at":"2026-03-09T06:32:54.559Z","repository":{"id":43784126,"uuid":"242977985","full_name":"tarantool/migrations","owner":"tarantool","description":null,"archived":false,"fork":false,"pushed_at":"2025-07-14T14:33:13.000Z","size":317,"stargazers_count":21,"open_issues_count":20,"forks_count":5,"subscribers_count":25,"default_branch":"master","last_synced_at":"2025-07-14T14:51:00.897Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tarantool.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-02-25T10:57:17.000Z","updated_at":"2024-09-16T17:49:03.000Z","dependencies_parsed_at":"2024-05-02T21:23:11.684Z","dependency_job_id":"05595c67-ebdc-4243-a612-0b9fb8646222","html_url":"https://github.com/tarantool/migrations","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/tarantool/migrations","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool%2Fmigrations","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool%2Fmigrations/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool%2Fmigrations/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool%2Fmigrations/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tarantool","download_url":"https://codeload.github.com/tarantool/migrations/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tarantool%2Fmigrations/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30284776,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T02:57:19.223Z","status":"ssl_error","status_checked_at":"2026-03-09T02:56:26.373Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-16T04:26:07.921Z","updated_at":"2026-03-09T06:32:54.525Z","avatar_url":"https://github.com/tarantool.png","language":"Lua","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Migrations manager for Tarantool Cartridge\n\n@lookup README.md\n\nMigrations module allows you to run cluster-wide migrations for your data.\n\nIt stores the list of applied migrations in cluster-wide config and applies resulting schema to cartridge `ddl`.\n\n## Usage\n\n1)  Add `migrations` dependency:\n    ```lua\n    -- \u003cproject-name\u003e-scm-1.rockspec\n        dependencies = {\n            ...\n            'migrations == \u003cthe-latest-tag\u003e-1',\n            ...\n        }\n    ```\n\n2) Add `migrator` to the list of cartridge roles in `init.lua`:\n    ```lua\n    -- init.lua\n    ....\n    cartridge.cfg({\n      roles = {\n        'migrator',\n        ....\n      }\n    })\n    ```\n\n3) Put migrations code to `./migrations` folder in your app. By default, migrator loads all files from it using lexicographical order.\nEvery migration (e. g. `0001_create_my_sharded_space_DATETIME.lua`) should expose a single parameter-less function `up`:\n    ```lua\n    return {\n        up = function()\n        local utils = require('migrator.utils')\n        local f = box.schema.create_space('my_sharded_space', {\n            format = {\n                { name = 'key', type = 'string' },\n                { name = 'bucket_id', type = 'unsigned' },\n                { name = 'value', type = 'any', is_nullable = true }\n            },\n            if_not_exists = true,\n        })\n        f:create_index('primary', {\n            parts = { 'key' },\n            if_not_exists = true,\n        })\n        f:create_index('bucket_id', {\n            parts = { 'bucket_id' },\n            if_not_exists = true,\n            unique = false\n        })\n        utils.register_sharding_key('my_sharded_space', {'key'})\n        return true\n        end\n    }\n    ```\n\n4) Call `curl -X POST http://\u003cyour_tarantool_ip\u003e:\u003chttp_port\u003e/migrations/up` once you are ready to migrate or connect to any instance of cluster and call `require('migrator').up()`.\n\n5) What will happen then:\n    * coordinator node (the one you curled upon) will trigger migrations execution on all replicaset leaders;\n    * each replicaset leader will apply all available migrations and reply to coordinator;\n    * each replicaset leader stores a list of applied migrations in a space;\n    * if all replies are successful, coordinator will apply changes to the resulting cluster ddl-schema.\n\n6) That's it!\n\n## Advanced usage\n\nIMPORTANT: code snippets below should be embedded to `init.lua`, so they would take effect on all nodes of the cluster.\n\n1) Change directory where migrations are located: embed the following to init.lua\n\n    ```lua\n    local migrator = require('migrator')\n    local my_directory_loader = require('migrator.directory-loader').new('test/integration/migrations')\n    migrator.set_loader(my_directory_loader)\n    ```\n\n2) ... or use `migrator.config-loader` to load migrations from Tarantool Cartridge clusterwide config.\n\n    Configure `migrator` to use `config-loader`:\n\n    ```lua\n    local migrator = require('migrator')\n    local config_loader = require('migrator.config-loader').new()\n    migrator.set_loader(config_loader)\n    ```\n\n    Navigate to Cartridge webui \"Code\" to write your migrations.\n    Migrations must be stored in *.lua files under \"migrations/source\" key:\n\n    ![config-loader example](doc/assets/config-loader.png)\n\n3) ... or use your own loader - it should expose a single function `list(self)` which returns a similar-looking array:\n\n    ```lua\n    local my_loader = {\n        list = function(_)\n            return {\n                {\n                    name  = '01_first',\n                    up = function() ... end\n                },\n            }\n        end\n    }\n    migrator.set_loader(my_loader)\n    ```\n\n4) Disable `cartridge.ddl` usage:\n\n    ```lua\n    migrator.set_use_cartridge_ddl(false)\n    ```\n\n    In this case, resulting schema will not be registered via `cartridge_set_schema`\n\n## Utils, helpers, tips and tricks\n* Specify a sharding key for `cartridge.ddl` (if you use it) using `utils.register_sharding_key`:\n  ```lua\n      up = function()\n          local utils = require('migrator.utils')\n          local f = box.schema.create_space('my_sharded_space', {\n              format = {\n                  { name = 'key', type = 'string' },\n                  { name = 'bucket_id', type = 'unsigned' },\n                  { name = 'value', type = 'any', is_nullable = true }\n              },\n              if_not_exists = true,\n          })\n          f:create_index('primary', {\n              parts = { 'key' },\n              if_not_exists = true,\n          })\n          f:create_index('bucket_id', {\n              parts = { 'bucket_id' },\n              if_not_exists = true,\n              unique = false\n          })\n          utils.register_sharding_key('my_sharded_space', {'key'})\n          return true\n      end\n  ```\n  Warning! It's not correct to specify 'bucket_id' as a 'key' parameter for register_sharding_key().\n  The 'bucket_id' field is a place where the output of sharding function is saved to.\n\n* Before 0.6.0, each storage migration run time was limited to 3600 seconds (#66).\n  If your migrations run longer than this limit, it will result in timeout error.\n\n  Starting with 0.6.0, you may configure this value with clusterwide config to\n  allow longer migrations. Default is 3600 seconds.\n  ```yaml\n  migrations:\n    options:\n      storage_timeout: 43200 # in seconds\n  ```\n\n* To run migrations code on a specific roles use `utils.check_roles_enabled`:\n    ```lua\n        up = function()\n            local utils = require('migrator.utils')\n            if utils.check_roles_enabled({'vshard-storage'}) then\n                local f = box.schema.create_space('my_sharded_space', {\n                    format = {\n                        { name = 'key', type = 'string' },\n                        { name = 'bucket_id', type = 'unsigned' },\n                        { name = 'value', type = 'any', is_nullable = true }\n                    },\n                    if_not_exists = true,\n                })\n                f:create_index('primary', {\n                    parts = { 'key' },\n                    if_not_exists = true,\n                })\n                f:create_index('bucket_id', {\n                    parts = { 'bucket_id' },\n                    if_not_exists = true,\n                    unique = false\n                })\n                utils.register_sharding_key('my_sharded_space', {'key'})\n                return true\n            elseif utils.check_roles_enabled({'my-role'}) then\n                my_specific_role_logic()\n            end\n        end\n    ```\n\n* To get a list of applied migrations make a GET request to\n  `http://\u003cyour_tarantool_ip\u003e:\u003chttp_port\u003e/migrations/applied` or call\n  `require('migrator').get_applied()` on any cluster instance. This method will return a list of\n  applied migrations grouped by a leader node.\n\n## Upgrade from 0.* versions.\n\nApplied migrations names storage method has been changed in `1.*` version: applied migrations list\nis stored on each cluster node separately in `_migrations` space. An additional step is required\nbefore applying migrations after update from `0.*`: call\n`curl -X POST http://\u003cyour_tarantool_ip\u003e:\u003chttp_port\u003e/migrations/move_migrations_state` or connect\nto any instance of cluster and call `require('migrator').move_migrations_state()`. This method\ndoes the following:\n\n- copies applied migrations names from cluster-wide configuration to the `_migrations` space on\n  leader nodes.\n- if copying is succeeded on all leaders, removes the list from the cluster-wide configuration.\n\n## Rolling back to 0.* versions.\n\nTo perform a downgrade from `1.*` to `0.*` version do the following:\n\n- get a list of the applied migrations using the `get_applied` API.\n- set list of migrations in cluster-wide config:\n```yaml\n    migrations:\n      applied:\n      - 01_migration.lua\n      - 02_migration.lua\n      . . .\n```\n- remove `_migrations` space and `_migrations_id_seq` on all nodes if necessary.\n- perform downgrade of the `migrations`.\n\n## Limitations\n- all migrations will be run on all cluster nodes (no partial migrations);\n- no pre-validation for migrations code (yet), so you should test them beforehands;\n- no support to run a single migration (yet);\n- no dry-run (yet);\n- no rolling back unsuccessful migrations (yet);\n- no migrating `down` (yet).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftarantool%2Fmigrations","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftarantool%2Fmigrations","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftarantool%2Fmigrations/lists"}