# Migration Style Guide
> 原文:[https://docs.gitlab.com/ee/development/migration_style_guide.html](https://docs.gitlab.com/ee/development/migration_style_guide.html)
* [Schema Changes](#schema-changes)
* [What Requires Downtime?](#what-requires-downtime)
* [Downtime Tagging](#downtime-tagging)
* [Reversibility](#reversibility)
* [Atomicity](#atomicity)
* [Heavy operations in a single transaction](#heavy-operations-in-a-single-transaction)
* [Retry mechanism when acquiring database locks](#retry-mechanism-when-acquiring-database-locks)
* [Examples](#examples)
* [When to use the helper method](#when-to-use-the-helper-method)
* [How the helper method works](#how-the-helper-method-works)
* [Multi-Threading](#multi-threading)
* [Removing indexes](#removing-indexes)
* [Adding indexes](#adding-indexes)
* [Adding foreign-key constraints](#adding-foreign-key-constraints)
* [`NOT NULL` constraints](#not-null-constraints)
* [Adding Columns With Default Values](#adding-columns-with-default-values)
* [Changing the column default](#changing-the-column-default)
* [Updating an existing column](#updating-an-existing-column)
* [Dropping a database table](#dropping-a-database-table)
* [Integer column type](#integer-column-type)
* [Strings and the Text data type](#strings-and-the-text-data-type)
* [Timestamp column type](#timestamp-column-type)
* [Storing JSON in database](#storing-json-in-database)
* [Testing](#testing)
* [Data migration](#data-migration)
* [Renaming reserved paths](#renaming-reserved-paths)
# Migration Style Guide[](#migration-style-guide "Permalink")
在為 GitLab 編寫遷移時,必須考慮到這些遷移將由成千上萬個各種規模的組織來運行,其中一些組織的數據庫中包含多年的數據.
此外,對于大多數組織來說,必須使服務器脫機以進行較小或較大的升級是一大負擔. 因此,重要的是要仔細編寫遷移文件,可以在線進行遷移,并遵守下面的樣式指南.
遷移**不準**要求 GitLab 設施采取離線,除非*絕對必要的* .
如果需要停機,則必須通過以下方式批準遷移:
1. 工程副總裁
2. 后端維護者
3. 數據庫維護者
可以在[https://about.gitlab.com/company/team/中](https://about.gitlab.com/company/team/)找到持有這些頭銜的人員的最新列表.
When writing your migrations, also consider that databases might have stale data or inconsistencies and guard for that. Try to make as few assumptions as possible about the state of the database.
請不要依賴特定于 GitLab 的代碼,因為它可能會在將來的版本中更改. 如果需要,將 GitLab 代碼復制粘貼到遷移中以使其向前兼容.
對于 GitLab.com,請考慮到在[部署 Canary](https://about.gitlab.com/handbook/engineering/infrastructure/library/canary/#configuration-and-deployment)之前運行常規遷移(在`db/migrate` ),在完成生產部署后運行部署后的遷移( `db/post_migrate` ).
## Schema Changes[](#schema-changes "Permalink")
Changes to the schema should be committed to `db/structure.sql`. This file is automatically generated by Rails, so you normally should not edit this file by hand. If your migration is adding a column to a table, that column will be added at the bottom. Please do not reorder columns manually for existing tables as this will cause confusing to other people using `db/structure.sql` generated by Rails.
當您的 GDK 中的本地數據庫與`master`數據庫中的模式不同時,可能很難將模式更改完全提交給 Git. 在這種情況下,您可以使用`scripts/regenerate-schema`腳本為要添加的遷移重新生成干凈的`db/structure.sql` . 該腳本將應用在`db/migrate`或`db/post_migrate`找到的所有遷移,因此,如果您不想將任何遷移提交到架構,請重命名或刪除它們. 如果您的分支不是以`master`為目標,則可以設置`TARGET`環境變量.
```
# Regenerate schema against `master`
scripts/regenerate-schema
# Regenerate schema against `12-9-stable-ee`
TARGET=12-9-stable-ee scripts/regenerate-schema
```
## What Requires Downtime?[](#what-requires-downtime "Permalink")
文檔["什么需要停機?"](what_requires_downtime.html) 指定各種數據庫操作,例如
* [dropping and renaming columns](what_requires_downtime.html#dropping-columns)
* [changing column constraints and types](what_requires_downtime.html#changing-column-constraints)
* [adding and dropping indexes, tables, and foreign keys](what_requires_downtime.html#adding-indexes)
以及他們是否需要停機,以及如何盡可能地進行停機.
## Downtime Tagging[](#downtime-tagging "Permalink")
每個遷移都必須指定是否需要停機,并且如果需要停機,還必須指定原因. 這是即使在遷移的 99%,不需要停機,因為這使得它更容易地發現, *確實*需要停機遷移所需.
要標記遷移,請在遷移類的主體中添加以下兩個常量:
* `DOWNTIME` :一個布爾值,當設置為`true`指示遷移需要停機時間.
* `DOWNTIME_REASON` :一個字符串,其中包含需要停機的遷移原因. 當`DOWNTIME`設置為`true`時, **必須**設置此常數.
例如:
```
class MyMigration < ActiveRecord::Migration[6.0]
DOWNTIME = true
DOWNTIME_REASON = 'This migration requires downtime because ...'
def change
...
end
end
```
如果遷移類中缺少`DOWNTIME`常量,則會出現錯誤(即 CI 將失敗).
## Reversibility[](#reversibility "Permalink")
您的遷移**必須是**可逆的. 這非常重要,因為在出??現漏洞或錯誤的情況下應該可以降級.
在遷移中,添加一條注釋,描述如何測試遷移的可逆性.
某些遷移無法撤消. 例如,某些數據遷移無法撤消,因為在遷移之前,我們丟失了有關數據庫狀態的信息. 您仍然應該創建一個帶注釋的`down`方法,解釋為什么不能撤消`up`方法執行的更改,以便即使撤消遷移期間執行的更改也可以撤消遷移本身:
```
def down
# no-op
# comment explaining why changes performed by `up` cannot be reversed.
end
```
## Atomicity[](#atomicity "Permalink")
默認情況下,遷移是單個事務. 即,在遷移開始時打開一個事務,并在處理完所有步驟后提交.
在單個事務中運行遷移可確保如果其中一個步驟失敗,則將不會執行任何步驟,從而使數據庫保持有效狀態. 因此,要么:
* 將所有遷移都放在一個單事務遷移中.
* 如有必要,將大多數操作放入一個遷移中,并為無法在單個事務中完成的步驟創建一個單獨的遷移.
例如,如果創建一個空表并需要為其建立索引,則建議使用常規的單事務遷移和默認的 rails schema 語句: [`add_index`](https://api.rubyonrails.org/v5.2/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_index) . 這是一個阻塞操作,但不會引起問題,因為該表尚未使用,因此它還沒有任何記錄.
## Heavy operations in a single transaction[](#heavy-operations-in-a-single-transaction "Permalink")
使用單事務遷移時,事務將在遷移期間保持數據庫連接,因此您必須確保遷移中的操作不會花費太多時間:通常,在遷移中執行的查詢需要在 GitLab.com 上舒適地放置`15s` .
如果您需要插入,更新或刪除大量數據,請執行以下操作:
* 必須使用`disable_ddl_transaction!`禁用單個事務`disable_ddl_transaction!` .
* 應該考慮在[后臺遷移中](background_migrations.html)這樣做.
## Retry mechanism when acquiring database locks[](#retry-mechanism-when-acquiring-database-locks "Permalink")
更改數據庫架構時,我們使用輔助方法來調用 DDL(數據定義語言)語句. 在某些情況下,這些 DDL 語句需要特定的數據庫鎖.
Example:
```
def change
remove_column :users, :full_name, :string
end
```
要執行此遷移,需要對`users`表進行排他鎖定. 當表被其他進程同時訪問和修改時,獲取鎖定可能需要一段時間. 鎖定請求正在隊列中等待,一旦進入隊列,它也可能阻止`users`表上的其他查詢.
有關 PostgresSQL 鎖的更多信息: [顯式鎖定](https://s0www0postgresql0org.icopy.site/docs/current/explicit-locking.html)
出于穩定性考慮,GitLab.com 設置了特定的[`statement_timeout`](../user/gitlab_com/index.html#postgresql) . 調用遷移時,任何數據庫查詢都將有固定的執行時間. 在最壞的情況下,請求將坐在鎖定隊列中,在配置的語句超時時間內阻止其他查詢,然后`canceling statement due to statement timeout`錯誤`canceling statement due to statement timeout`失敗.
此問題可能導致應用程序升級過程失敗,甚至導致應用程序穩定性問題,因為該表可能會在短時間內無法訪問.
為了提高數據庫遷移的可靠性和穩定性,GitLab 代碼庫提供了一種輔助方法,以使用不同的`lock_timeout`設置重試操作,并在兩次嘗試之間等待時間. 為了獲得必要的鎖定,進行了多次較小的嘗試,從而使數據庫可以處理其他語句.
### Examples[](#examples "Permalink")
**刪除列:**
```
include Gitlab::Database::MigrationHelpers
def up
with_lock_retries do
remove_column :users, :full_name
end
end
def down
with_lock_retries do
add_column :users, :full_name, :string
end
end
```
**刪除外鍵:**
```
include Gitlab::Database::MigrationHelpers
def up
with_lock_retries do
remove_foreign_key :issues, :projects
end
end
def down
with_lock_retries do
add_foreign_key :issues, :projects
end
end
```
**更改列的默認值:**
```
include Gitlab::Database::MigrationHelpers
def up
with_lock_retries do
change_column_default :merge_requests, :lock_version, from: nil, to: 0
end
end
def down
with_lock_retries do
change_column_default :merge_requests, :lock_version, from: 0, to: nil
end
end
```
**用外鍵創建一個新表:**
我們可以簡單地用`with_lock_retries`包裝`create_table`方法:
```
def up
with_lock_retries do
create_table :issues do |t|
t.references :project, index: true, null: false, foreign_key: { on_delete: :cascade }
t.string :title, limit: 255
end
end
end
def down
drop_table :issues
end
```
**當我們有兩個外鍵時創建一個新表:**
為此,我們需要進行三個遷移:
1. 創建不帶外鍵(帶有索引)的表.
2. 將外鍵添加到第一個表.
3. 將外鍵添加到第二個表.
創建表:
```
def up
create_table :imports do |t|
t.bigint :project_id, null: false
t.bigint :user_id, null: false
t.string :jid, limit: 255
end
add_index :imports, :project_id
add_index :imports, :user_id
end
def down
drop_table :imports
end
```
在`projects`添加外鍵:
```
include Gitlab::Database::MigrationHelpers
def up
with_lock_retries do
add_foreign_key :imports, :projects, column: :project_id, on_delete: :cascade
end
end
def down
with_lock_retries do
remove_foreign_key :imports, column: :project_id
end
end
```
向`users`添加外鍵:
```
include Gitlab::Database::MigrationHelpers
def up
with_lock_retries do
add_foreign_key :imports, :users, column: :user_id, on_delete: :cascade
end
end
def down
with_lock_retries do
remove_foreign_key :imports, column: :user_id
end
end
```
**與`disable_ddl_transaction!`配合使用`disable_ddl_transaction!`**
通常, `with_lock_retries`幫助程序應與`disabled_ddl_transaction!` . 定制的 RuboCop 規則可確保只能將允許的方法放在鎖重試塊中.
```
disable_ddl_transaction!
def up
with_lock_retries do
add_column :users, :name, :text
end
add_text_limit :users, :name, 255 # Includes constraint validation (full table scan)
end
```
RuboCop 規則通常允許使用下面列出的標準 Rails 遷移方法. 此示例將導致 Rubocop 犯罪:
```
disabled_ddl_transaction!
def up
with_lock_retries do
add_concurrent_index :users, :name
end
end
```
### When to use the helper method[](#when-to-use-the-helper-method "Permalink")
通常使用標準的 Rails 遷移幫助器方法時,可以使用`with_lock_retries`幫助器方法. 如果在同一個表上執行多個遷移助手,則調用它們不是問題.
當數據庫遷移涉及高流量表之一時,建議使用`with_lock_retries`幫助程序方法:
* `users`
* `projects`
* `namespaces`
* `issues`
* `merge_requests`
* `ci_pipelines`
* `ci_builds`
* `notes`
更改示例:
* `add_foreign_key` / `remove_foreign_key`
* `add_column` / `remove_column`
* `change_column_default`
* `create_table` / `drop_table`
**注意:** `with_lock_retries`方法**不能**在`change`方法中使用,必須手動定義`up`和`down`方法以使遷移可逆.
### How the helper method works[](#how-the-helper-method-works "Permalink")
1. 重復 50 次.
2. 對于每次迭代,設置一個預配置的`lock_timeout` .
3. 嘗試執行給定的塊. ( `remove_column` ).
4. 如果出現`LockWaitTimeout`錯誤,請為預配置的`sleep_time`睡眠,然后重試該塊.
5. 如果未引發錯誤,則當前迭代已成功執行該塊.
有關更多信息,請檢查[`Gitlab::Database::WithLockRetries`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/database/with_lock_retries.rb)類. `with_lock_retries`幫助器方法在[`Gitlab::Database::MigrationHelpers`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/database/migration_helpers.rb)模塊中實現.
在最壞的情況下,該方法:
* 在 40 分鐘內最多執行 50 次該塊.
* 大部分時間都花費在每次迭代后的預先配置的睡眠時段中.
* 第 50 次重試之后,將像標準遷移調用一樣在沒有`lock_timeout`情況下執行該塊.
* 如果無法獲取鎖,則遷移將失敗,并出現`statement timeout`錯誤.
如果訪問`users`表的事務運行時間很長(超過 40 分鐘),則遷移可能會失敗.
## Multi-Threading[](#multi-threading "Permalink")
有時,遷移可能需要使用多個 Ruby 線程來加快遷移速度. 為此,您的遷移需要包括模塊`Gitlab::Database::MultiThreadedMigration` :
```
class MyMigration < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
include Gitlab::Database::MultiThreadedMigration
end
```
然后可以使用`with_multiple_threads`方法在單獨的線程中執行工作. 例如:
```
class MyMigration < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
include Gitlab::Database::MultiThreadedMigration
def up
with_multiple_threads(4) do
disable_statement_timeout
# ...
end
end
end
```
在這里,對`disable_statement_timeout`的調用將使用`with_multiple_threads`塊本地的連接,而不是重新使用全局連接池. 這樣可以確保每個線程都有自己的連接對象,并且在嘗試獲取一個連接對象時不會超時.
**注意:** PostgreSQL 具有允許的最大連接數. 此限制因安裝而異. 因此,建議您一次遷移不要使用超過 32 個線程. 通常,4-8 個線程應該綽綽有余.
## Removing indexes[](#removing-indexes "Permalink")
如果刪除索引時表不為空,請確保使用方法`remove_concurrent_index`而不是常規的`remove_index`方法. `remove_concurrent_index`方法同時刪除索引,因此不需要鎖定,也無需停機. 要使用此方法,必須通過調用方法`disable_ddl_transaction!`禁用單交易模式`disable_ddl_transaction!` 在您的遷移類的主體中,如下所示:
```
class MyMigration < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
def up
remove_concurrent_index :table_name, :column_name
end
end
```
請注意,在刪除索引之前不必檢查索引是否存在.
對于較小的表(例如空表或少于`1,000`條記錄的表),建議在單事務遷移中使用`remove_index` ,并將其與不需要`disable_ddl_transaction!`其他操作結合使用`disable_ddl_transaction!` .
## Adding indexes[](#adding-indexes "Permalink")
在添加索引之前,請考慮該索引是否必要. 在某些情況下可能不需要索引,例如:
* 該表很小(少于`1,000`條記錄),并且預計大小不會成倍增長.
* 任何現有索引都會過濾掉足夠的行.
* 添加索引后查詢時間的減少并不明顯.
另外,不需要寬索引來匹配查詢的所有過濾條件,我們只需要覆蓋足夠多的列即可使索引查找具有足夠小的選擇性. 請查看我們的[添加數據庫索引](adding_database_indexes.html)指南以獲取更多詳細信息.
將索引添加到非空表時,請確保使用方法`add_concurrent_index`而不是常規的`add_index`方法. 使用 PostgreSQL 時, `add_concurrent_index`方法自動創建并發索引,從而消除了停機時間.
要使用此方法,必須通過調用方法`disable_ddl_transaction!`禁用單交易模式`disable_ddl_transaction!` 在您的遷移類的主體中,如下所示:
```
class MyMigration < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :table, :column
end
def down
remove_concurrent_index :table, :column
end
end
```
如果需要添加唯一索引,請記住,數據庫中可能存在現有重復項. 這意味著在添加唯一索引之前,應始終*首先*添加一個刪除所有重復項的遷移.
對于小型表(例如空表或少于`1,000`條記錄的表),建議在單事務遷移中使用`add_index` ,并將其與不需要`disable_ddl_transaction!`其他操作結合使用`disable_ddl_transaction!` .
## Adding foreign-key constraints[](#adding-foreign-key-constraints "Permalink")
向現有列或新列添加外鍵約束時,還請記住在該列上添加索引.
這是所有外鍵所**必需**的,例如,為了支持有效的級聯刪除:當刪除表中的許多行時,也需要刪除引用的記錄. 數據庫必須在引用的表中查找相應的記錄. 沒有索引,這將導致對表進行順序掃描,這可能需要很長時間.
這是一個示例,其中我們添加了一個帶有外鍵約束的新列. 請注意,它包括`index: true`為其創建索引.
```
class Migration < ActiveRecord::Migration[6.0]
def change
add_reference :model, :other_model, index: true, foreign_key: { on_delete: :cascade }
end
end
```
當向非空表中的現有列添加外鍵約束時,我們必須使用`add_concurrent_foreign_key`和`add_concurrent_index`而不是`add_reference` .
對于空表(例如新表),建議在單事務遷移中使用`add_reference` ,并將其與不需要`disable_ddl_transaction!`其他操作結合使用`disable_ddl_transaction!` .
您可以閱讀有關將[外鍵約束](database/add_foreign_key_to_existing_column.html)添加[到現有列的更多信息](database/add_foreign_key_to_existing_column.html) .
## `NOT NULL` constraints[](#not-null-constraints "Permalink")
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/38358) in GitLab 13.0.
有關更多信息,請參見關于[`NOT NULL`約束](database/not_null_constraints.html)的樣式指南.
## Adding Columns With Default Values[](#adding-columns-with-default-values "Permalink")
PostgreSQL 11 是自 GitLab 13.0 以來的最低版本,添加具有默認值的列變得更加容易,并且在所有情況下都應使用標準的`add_column`幫助器.
在 PostgreSQL 11 之前,添加帶有默認值的列是有問題的,因為這會導致全表重寫. 相應的幫助程序`add_column_with_default`已被棄用,并將在以后的版本中刪除.
**注意:**如果%12.9 或更早版本需要向后端口添加具有默認值的列,則應使用`add_column_with_default`幫助器. 如果涉及[大表](https://gitlab.com/gitlab-org/gitlab/-/blob/master/rubocop/rubocop-migrations.yml#L3) ,則禁止向后移植到%12.9.
## Changing the column default[](#changing-the-column-default "Permalink")
可能有人認為,使用`change_column_default`更改默認列對于較大的表來說是一項昂貴且破壞性的操作,但實際上并非如此.
以以下遷移為例:
```
class DefaultRequestAccessGroups < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
change_column_default(:namespaces, :request_access_enabled, from: false, to: true)
end
end
```
上面的遷移更改了我們最大的表之一的默認列值: `namespaces` . 可以將其翻譯為:
```
ALTER TABLE namespaces
ALTER COLUMN request_access_enabled
DEFAULT false
```
在這種情況下,默認值存在,我們只是更改了`request_access_enabled`列的元數據,這并不意味著重寫`namespaces`表中的所有現有記錄. 僅當使用默認值創建新列時,所有記錄才會被重寫.
**注意:** PostgresSQL 11.0 引入[了](https://www.depesz.com/2018/04/04/waiting-for-postgresql-11-fast-alter-table-add-column-with-a-non-null-default/)更快的[ALTER TABLE ADD COLUMN(具有非空默認值](https://www.depesz.com/2018/04/04/waiting-for-postgresql-11-fast-alter-table-add-column-with-a-non-null-default/) ),從而消除了在添加具有默認值的新列時重寫表的需求.
由于上述原因,在單事務遷移中使用`change_column_default`是安全的,而不需要`disable_ddl_transaction!` .
## Updating an existing column[](#updating-an-existing-column "Permalink")
要將現有列更新為特定值,可以使用`update_column_in_batches` . 這會將更新分為批次,因此我們不會在單個語句中更新太多行.
這`some_column` `projects`表中的`foo`列更新為 10,其中`some_column`為`'hello'` :
```
update_column_in_batches(:projects, :foo, 10) do |table, query|
query.where(table[:some_column].eq('hello'))
end
```
如果需要計算的更新,則可以將值包裝在`Arel.sql` ,因此 Arel 將其視為 SQL 文字. 這也是[Rails 6](https://gitlab.com/gitlab-org/gitlab/-/issues/28497)的必需棄用.
下面的示例與上面的示例相同,但是該值設置為`bar`和`baz`列的乘積:
```
update_value = Arel.sql('bar * baz')
update_column_in_batches(:projects, :foo, update_value) do |table, query|
query.where(table[:some_column].eq('hello'))
end
```
像`add_column_with_default`一樣,有一個 RuboCop cop 可以檢測大表上的用法. 在`update_column_in_batches`的情況下,可以在大表上運行,只要它僅更新表中行的一小部分,而在未在 GitLab.com 登臺環境上進行驗證的情況下不要忽略它-或事先請別人為您這樣做.
## Dropping a database table[](#dropping-a-database-table "Permalink")
刪除數據庫表并不常見,Rails 提供的`drop_table`方法通常被認為是安全的. 刪除表格之前,請考慮以下因素:
如果您的表在高流量表上具有外鍵(如`projects` ),則`DROP TABLE`語句可能會失敗,并出現**語句超時**錯誤. 確定哪些表是高流量可能很困難. 自我管理的實例可能會以不同的使用模式使用 GitLab 的不同功能,因此僅基于 GitLab.com 進行假設是不夠的.
表**沒有記錄** (功能從未使用過),也**沒有外鍵** :
* 只需在遷移中使用`drop_table`方法即可.
```
def change
drop_table :my_table
end
```
Table **有記錄** but **沒有外鍵**:
* 第一版:刪除與表相關的應用程序代碼,例如模型,控制器和服務.
* 第二版:在遷移中使用`drop_table`方法.
```
def up
drop_table :my_table
end
def down
# create_table ...
end
```
Table **有外鍵**:
* 第一版:刪除與表相關的應用程序代碼,例如模型,控制器和服務.
* 第二版:使用`with_lock_retries`幫助器方法刪除外鍵. 在另一個遷移文件中使用`drop_table` .
**第二個版本的遷移:**
刪除`projects`表上的外鍵:
```
# first migration file
def up
with_lock_retries do
remove_foreign_key :my_table, :projects
end
end
def down
with_lock_retries do
add_foreign_key :my_table, :projects
end
end
```
放下桌子:
```
# second migration file
def up
drop_table :my_table
end
def down
# create_table ...
end
```
## Integer column type[](#integer-column-type "Permalink")
默認情況下,整數列最多可容納 4 個字節(32 位)的數字. 最大值為 2,147,483,647\. 創建一個以字節為單位保存文件大小的列時,請注意這一點. 如果以字節為單位跟蹤文件大小,這會將最大文件大小限制為剛好超過 2GB.
要允許整數列最多容納 8 個字節(64 位)的數字,請將限制明確設置為 8 個字節. 這將使該列最多`9,223,372,036,854,775,807`個值.
Rails 遷移示例:
```
add_column(:projects, :foo, :integer, default: 10, limit: 8)
```
## Strings and the Text data type[](#strings-and-the-text-data-type "Permalink")
在 GitLab 13.0 中[引入](https://gitlab.com/gitlab-org/gitlab/-/issues/30453) .
有關更多信息,請參見[文本數據類型](database/strings_and_the_text_data_type.html)樣式指南.
## Timestamp column type[](#timestamp-column-type "Permalink")
默認情況下,Rails 使用`timestamp`數據類型來存儲沒有時區信息的時間戳數據. 通過調用`add_timestamps`或`timestamps`方法來使用`timestamp`數據類型.
另外,Rails 將`:datetime`數據類型轉換為`timestamp`一.
Example:
```
# timestamps
create_table :users do |t|
t.timestamps
end
# add_timestamps
def up
add_timestamps :users
end
# :datetime
def up
add_column :users, :last_sign_in, :datetime
end
```
代替使用這些方法,應該使用以下方法來存儲帶有時區的時間戳:
* `add_timestamps_with_timezone`
* `timestamps_with_timezone`
* `datetime_with_timezone`
這樣可以確保所有時間戳都有指定的時區. 反過來,這意味著當系統的時區更改時,現有時間戳不會突然使用其他時區. 這也使得非常清楚,首先使用了哪個時區.
## Storing JSON in database[](#storing-json-in-database "Permalink")
Rails 5 本機支持`JSONB` (二進制 JSON)列類型. 添加此列的示例遷移:
```
class AddOptionsToBuildMetadata < ActiveRecord::Migration[5.0]
DOWNTIME = false
def change
add_column :ci_builds_metadata, :config_options, :jsonb
end
end
```
您必須使用序列化器來提供翻譯層:
```
class BuildMetadata
serialize :config_options, Serializers::JSON # rubocop:disable Cop/ActiveRecordSerialize
end
```
使用`JSONB`列時,請使用[JsonSchemaValidator](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/validators/json_schema_validator.rb)來控制隨時間推移插入的數據.
```
class BuildMetadata
validates :config_options, json_schema: { filename: 'build_metadata_config_option' }
end
```
## Testing[](#testing "Permalink")
請參閱" [測試 Rails 遷移](testing_guide/testing_migrations_guide.html)樣式"指南.
## Data migration[](#data-migration "Permalink")
請比一般 ActiveRecord 語法更喜歡 Arel 和普通 SQL. 如果使用普通 SQL,則需要使用`quote_string` helper 手動引用所有輸入.
Arel 的示例:
```
users = Arel::Table.new(:users)
users.group(users[:user_id]).having(users[:id].count.gt(5))
#update other tables with these results
```
帶有普通 SQL 和`quote_string`幫助器的示例:
```
select_all("SELECT name, COUNT(id) as cnt FROM tags GROUP BY name HAVING COUNT(id) > 1").each do |tag|
tag_name = quote_string(tag["name"])
duplicate_ids = select_all("SELECT id FROM tags WHERE name = '#{tag_name}'").map{|tag| tag["id"]}
origin_tag_id = duplicate_ids.first
duplicate_ids.delete origin_tag_id
execute("UPDATE taggings SET tag_id = #{origin_tag_id} WHERE tag_id IN(#{duplicate_ids.join(",")})")
execute("DELETE FROM tags WHERE id IN(#{duplicate_ids.join(",")})")
end
```
如果需要更復雜的邏輯,則可以定義和使用遷移本地模型. 例如:
```
class MyMigration < ActiveRecord::Migration[6.0]
class Project < ActiveRecord::Base
self.table_name = 'projects'
end
def up
# Reset the column information of all the models that update the database
# to ensure the Active Record's knowledge of the table structure is current
Project.reset_column_information
# ... ...
end
end
```
這樣做時,請確保顯式設置模型的表名,這樣它就不會派生自類名或名稱空間.
最后,對于所有更新數據庫的本地模型,請確保在遷移的`up`方法中運行`reset_column_information` .
這樣做的原因是所有遷移類都在開始時加載( `db:migrate`啟動時),因此,如果另一個遷移更新了該模式,它們將與它們映射的表模式不同步. 這將導致在嘗試插入基礎表或對其進行更新時數據遷移失敗,因為`ActiveRecord`將新列報告為`unknown attribute` .
### Renaming reserved paths[](#renaming-reserved-paths "Permalink")
引入新的項目路線時,它可能與任何現有記錄沖突. 這些記錄的路徑應重命名,并且相關數據應在磁盤上移動.
由于我們已經必須做幾次,因此現在有一些幫助程序可以幫助您.
要使用此功能,您可以在遷移中包括`Gitlab::Database::RenameReservedPathsMigration::V1` . 這將提供 3 種方法,您可以通過一種或多種需要拒絕的路徑.
**`rename_root_paths`** :這將使用給定名稱重命名所有沒有`parent_id`名稱*空間*的路徑.
**`rename_child_paths`** :這將使用給定名稱重命名所有具有`parent_id`名稱*空間*的路徑.
**`rename_wildcard_paths`** :這將重命名所有*項目*的路徑以及所有具有`project_id` *命名空間* .
這些行的`path`列將重命名為其先前的值,后跟一個整數. 例如: `users`將變成`users0`
- GitLab Docs
- Installation
- Requirements
- GitLab cloud native Helm Chart
- Install GitLab with Docker
- Installation from source
- Install GitLab on Microsoft Azure
- Installing GitLab on Google Cloud Platform
- Installing GitLab on Amazon Web Services (AWS)
- Analytics
- Code Review Analytics
- Productivity Analytics
- Value Stream Analytics
- Kubernetes clusters
- Adding and removing Kubernetes clusters
- Adding EKS clusters
- Adding GKE clusters
- Group-level Kubernetes clusters
- Instance-level Kubernetes clusters
- Canary Deployments
- Cluster Environments
- Deploy Boards
- GitLab Managed Apps
- Crossplane configuration
- Cluster management project (alpha)
- Kubernetes Logs
- Runbooks
- Serverless
- Deploying AWS Lambda function using GitLab CI/CD
- Securing your deployed applications
- Groups
- Contribution Analytics
- Custom group-level project templates
- Epics
- Manage epics
- Group Import/Export
- Insights
- Issues Analytics
- Iterations
- Public access
- SAML SSO for GitLab.com groups
- SCIM provisioning using SAML SSO for GitLab.com groups
- Subgroups
- Roadmap
- Projects
- GitLab Secure
- Security Configuration
- Container Scanning
- Dependency Scanning
- Dependency List
- Static Application Security Testing (SAST)
- Secret Detection
- Dynamic Application Security Testing (DAST)
- GitLab Security Dashboard
- Offline environments
- Standalone Vulnerability pages
- Security scanner integration
- Badges
- Bulk editing issues and merge requests at the project level
- Code Owners
- Compliance
- License Compliance
- Compliance Dashboard
- Create a project
- Description templates
- Deploy Keys
- Deploy Tokens
- File finder
- Project integrations
- Integrations
- Atlassian Bamboo CI Service
- Bugzilla Service
- Custom Issue Tracker service
- Discord Notifications service
- Enabling emails on push
- GitHub project integration
- Hangouts Chat service
- Atlassian HipChat
- Irker IRC Gateway
- GitLab Jira integration
- Mattermost Notifications Service
- Mattermost slash commands
- Microsoft Teams service
- Mock CI Service
- Prometheus integration
- Redmine Service
- Slack Notifications Service
- Slack slash commands
- GitLab Slack application
- Webhooks
- YouTrack Service
- Insights
- Issues
- Crosslinking Issues
- Design Management
- Confidential issues
- Due dates
- Issue Boards
- Issue Data and Actions
- Labels
- Managing issues
- Milestones
- Multiple Assignees for Issues
- Related issues
- Service Desk
- Sorting and ordering issue lists
- Issue weight
- Associate a Zoom meeting with an issue
- Merge requests
- Allow collaboration on merge requests across forks
- Merge Request Approvals
- Browser Performance Testing
- How to create a merge request
- Cherry-pick changes
- Code Quality
- Load Performance Testing
- Merge Request dependencies
- Fast-forward merge requests
- Merge when pipeline succeeds
- Merge request conflict resolution
- Reverting changes
- Reviewing and managing merge requests
- Squash and merge
- Merge requests versions
- Draft merge requests
- Members of a project
- Migrating projects to a GitLab instance
- Import your project from Bitbucket Cloud to GitLab
- Import your project from Bitbucket Server to GitLab
- Migrating from ClearCase
- Migrating from CVS
- Import your project from FogBugz to GitLab
- Gemnasium
- Import your project from GitHub to GitLab
- Project importing from GitLab.com to your private GitLab instance
- Import your project from Gitea to GitLab
- Import your Jira project issues to GitLab
- Migrating from Perforce Helix
- Import Phabricator tasks into a GitLab project
- Import multiple repositories by uploading a manifest file
- Import project from repo by URL
- Migrating from SVN to GitLab
- Migrating from TFVC to Git
- Push Options
- Releases
- Repository
- Branches
- Git Attributes
- File Locking
- Git file blame
- Git file history
- Repository mirroring
- Protected branches
- Protected tags
- Push Rules
- Reduce repository size
- Signing commits with GPG
- Syntax Highlighting
- GitLab Web Editor
- Web IDE
- Requirements Management
- Project settings
- Project import/export
- Project access tokens (Alpha)
- Share Projects with other Groups
- Snippets
- Static Site Editor
- Wiki
- Project operations
- Monitor metrics for your CI/CD environment
- Set up alerts for Prometheus metrics
- Embedding metric charts within GitLab-flavored Markdown
- Embedding Grafana charts
- Using the Metrics Dashboard
- Dashboard YAML properties
- Metrics dashboard settings
- Panel types for dashboards
- Using Variables
- Templating variables for metrics dashboards
- Prometheus Metrics library
- Monitoring AWS Resources
- Monitoring HAProxy
- Monitoring Kubernetes
- Monitoring NGINX
- Monitoring NGINX Ingress Controller
- Monitoring NGINX Ingress Controller with VTS metrics
- Alert Management
- Error Tracking
- Tracing
- Incident Management
- GitLab Status Page
- Feature Flags
- GitLab CI/CD
- GitLab CI/CD pipeline configuration reference
- GitLab CI/CD include examples
- Introduction to CI/CD with GitLab
- Getting started with GitLab CI/CD
- How to enable or disable GitLab CI/CD
- Using SSH keys with GitLab CI/CD
- Migrating from CircleCI
- Migrating from Jenkins
- Auto DevOps
- Getting started with Auto DevOps
- Requirements for Auto DevOps
- Customizing Auto DevOps
- Stages of Auto DevOps
- Upgrading PostgreSQL for Auto DevOps
- Cache dependencies in GitLab CI/CD
- GitLab ChatOps
- Cloud deployment
- Docker integration
- Building Docker images with GitLab CI/CD
- Using Docker images
- Building images with kaniko and GitLab CI/CD
- GitLab CI/CD environment variables
- Predefined environment variables reference
- Where variables can be used
- Deprecated GitLab CI/CD variables
- Environments and deployments
- Protected Environments
- GitLab CI/CD Examples
- Test a Clojure application with GitLab CI/CD
- Using Dpl as deployment tool
- Testing a Phoenix application with GitLab CI/CD
- End-to-end testing with GitLab CI/CD and WebdriverIO
- DevOps and Game Dev with GitLab CI/CD
- Deploy a Spring Boot application to Cloud Foundry with GitLab CI/CD
- How to deploy Maven projects to Artifactory with GitLab CI/CD
- Testing PHP projects
- Running Composer and NPM scripts with deployment via SCP in GitLab CI/CD
- Test and deploy Laravel applications with GitLab CI/CD and Envoy
- Test and deploy a Python application with GitLab CI/CD
- Test and deploy a Ruby application with GitLab CI/CD
- Test and deploy a Scala application to Heroku
- GitLab CI/CD for external repositories
- Using GitLab CI/CD with a Bitbucket Cloud repository
- Using GitLab CI/CD with a GitHub repository
- GitLab Pages
- GitLab Pages
- GitLab Pages domain names, URLs, and baseurls
- Create a GitLab Pages website from scratch
- Custom domains and SSL/TLS Certificates
- GitLab Pages integration with Let's Encrypt
- GitLab Pages Access Control
- Exploring GitLab Pages
- Incremental Rollouts with GitLab CI/CD
- Interactive Web Terminals
- Optimizing GitLab for large repositories
- Metrics Reports
- CI/CD pipelines
- Pipeline Architecture
- Directed Acyclic Graph
- Multi-project pipelines
- Parent-child pipelines
- Pipelines for Merge Requests
- Pipelines for Merged Results
- Merge Trains
- Job artifacts
- Pipeline schedules
- Pipeline settings
- Triggering pipelines through the API
- Review Apps
- Configuring GitLab Runners
- GitLab CI services examples
- Using MySQL
- Using PostgreSQL
- Using Redis
- Troubleshooting CI/CD
- GitLab Package Registry
- GitLab Container Registry
- Dependency Proxy
- GitLab Composer Repository
- GitLab Conan Repository
- GitLab Maven Repository
- GitLab NPM Registry
- GitLab NuGet Repository
- GitLab PyPi Repository
- API Docs
- API resources
- .gitignore API
- GitLab CI YMLs API
- Group and project access requests API
- Appearance API
- Applications API
- Audit Events API
- Avatar API
- Award Emoji API
- Project badges API
- Group badges API
- Branches API
- Broadcast Messages API
- Project clusters API
- Group clusters API
- Instance clusters API
- Commits API
- Container Registry API
- Custom Attributes API
- Dashboard annotations API
- Dependencies API
- Deploy Keys API
- Deployments API
- Discussions API
- Dockerfiles API
- Environments API
- Epics API
- Events
- Feature Flags API
- Feature flag user lists API
- Freeze Periods API
- Geo Nodes API
- Group Activity Analytics API
- Groups API
- Import API
- Issue Boards API
- Group Issue Boards API
- Issues API
- Epic Issues API
- Issues Statistics API
- Jobs API
- Keys API
- Labels API
- Group Labels API
- License
- Licenses API
- Issue links API
- Epic Links API
- Managed Licenses API
- Markdown API
- Group and project members API
- Merge request approvals API
- Merge requests API
- Project milestones API
- Group milestones API
- Namespaces API
- Notes API
- Notification settings API
- Packages API
- Pages domains API
- Pipeline schedules API
- Pipeline triggers API
- Pipelines API
- Project Aliases API
- Project import/export API
- Project repository storage moves API
- Project statistics API
- Project templates API
- Projects API
- Protected branches API
- Protected tags API
- Releases API
- Release links API
- Repositories API
- Repository files API
- Repository submodules API
- Resource label events API
- Resource milestone events API
- Resource weight events API
- Runners API
- SCIM API
- Search API
- Services API
- Application settings API
- Sidekiq Metrics API
- Snippets API
- Project snippets
- Application statistics API
- Suggest Changes API
- System hooks API
- Tags API
- Todos API
- Users API
- Project-level Variables API
- Group-level Variables API
- Version API
- Vulnerabilities API
- Vulnerability Findings API
- Wikis API
- GraphQL API
- Getting started with GitLab GraphQL API
- GraphQL API Resources
- API V3 to API V4
- Validate the .gitlab-ci.yml (API)
- User Docs
- Abuse reports
- User account
- Active sessions
- Deleting a User account
- Permissions
- Personal access tokens
- Profile preferences
- Threads
- GitLab and SSH keys
- GitLab integrations
- Git
- GitLab.com settings
- Infrastructure as code with Terraform and GitLab
- GitLab keyboard shortcuts
- GitLab Markdown
- AsciiDoc
- GitLab Notification Emails
- GitLab Quick Actions
- Autocomplete characters
- Reserved project and group names
- Search through GitLab
- Advanced Global Search
- Advanced Syntax Search
- Time Tracking
- GitLab To-Do List
- Administrator Docs
- Reference architectures
- Reference architecture: up to 1,000 users
- Reference architecture: up to 2,000 users
- Reference architecture: up to 3,000 users
- Reference architecture: up to 5,000 users
- Reference architecture: up to 10,000 users
- Reference architecture: up to 25,000 users
- Reference architecture: up to 50,000 users
- Troubleshooting a reference architecture set up
- Working with the bundled Consul service
- Configuring PostgreSQL for scaling
- Configuring GitLab application (Rails)
- Load Balancer for multi-node GitLab
- Configuring a Monitoring node for Scaling and High Availability
- NFS
- Working with the bundled PgBouncer service
- Configuring Redis for scaling
- Configuring Sidekiq
- Admin Area settings
- Continuous Integration and Deployment Admin settings
- Custom instance-level project templates
- Diff limits administration
- Enable and disable GitLab features deployed behind feature flags
- Geo nodes Admin Area
- GitLab Pages administration
- Health Check
- Job logs
- Labels administration
- Log system
- PlantUML & GitLab
- Repository checks
- Repository storage paths
- Repository storage types
- Account and limit settings
- Service templates
- System hooks
- Changing your time zone
- Uploads administration
- Abuse reports
- Activating and deactivating users
- Audit Events
- Blocking and unblocking users
- Broadcast Messages
- Elasticsearch integration
- Gitaly
- Gitaly Cluster
- Gitaly reference
- Monitoring GitLab
- Monitoring GitLab with Prometheus
- Performance Bar
- Usage statistics
- Object Storage
- Performing Operations in GitLab
- Cleaning up stale Redis sessions
- Fast lookup of authorized SSH keys in the database
- Filesystem Performance Benchmarking
- Moving repositories managed by GitLab
- Run multiple Sidekiq processes
- Sidekiq MemoryKiller
- Switching to Puma
- Understanding Unicorn and unicorn-worker-killer
- User lookup via OpenSSH's AuthorizedPrincipalsCommand
- GitLab Package Registry administration
- GitLab Container Registry administration
- Replication (Geo)
- Geo database replication
- Geo with external PostgreSQL instances
- Geo configuration
- Using a Geo Server
- Updating the Geo nodes
- Geo with Object storage
- Docker Registry for a secondary node
- Geo for multiple nodes
- Geo security review (Q&A)
- Location-aware Git remote URL with AWS Route53
- Tuning Geo
- Removing secondary Geo nodes
- Geo data types support
- Geo Frequently Asked Questions
- Geo Troubleshooting
- Geo validation tests
- Disaster Recovery (Geo)
- Disaster recovery for planned failover
- Bring a demoted primary node back online
- Automatic background verification
- Rake tasks
- Back up and restore GitLab
- Clean up
- Namespaces
- Maintenance Rake tasks
- Geo Rake Tasks
- GitHub import
- Import bare repositories
- Integrity check Rake task
- LDAP Rake tasks
- Listing repository directories
- Praefect Rake tasks
- Project import/export administration
- Repository storage Rake tasks
- Generate sample Prometheus data
- Uploads migrate Rake tasks
- Uploads sanitize Rake tasks
- User management
- Webhooks administration
- X.509 signatures
- Server hooks
- Static objects external storage
- Updating GitLab
- GitLab release and maintenance policy
- Security
- Password Storage
- Custom password length limits
- Restrict allowed SSH key technologies and minimum length
- Rate limits
- Webhooks and insecure internal web services
- Information exclusivity
- How to reset your root password
- How to unlock a locked user from the command line
- User File Uploads
- How we manage the TLS protocol CRIME vulnerability
- User email confirmation at sign-up
- Security of running jobs
- Proxying assets
- CI/CD Environment Variables
- Contributor and Development Docs
- Contribute to GitLab
- Community members & roles
- Implement design & UI elements
- Issues workflow
- Merge requests workflow
- Code Review Guidelines
- Style guides
- GitLab Architecture Overview
- CI/CD development documentation
- Database guides
- Database Review Guidelines
- Database Review Guidelines
- Migration Style Guide
- What requires downtime?
- Understanding EXPLAIN plans
- Rake tasks for developers
- Mass inserting Rails models
- GitLab Documentation guidelines
- Documentation Style Guide
- Documentation structure and template
- Documentation process
- Documentation site architecture
- Global navigation
- GitLab Docs monthly release process
- Telemetry Guide
- Usage Ping Guide
- Snowplow Guide
- Experiment Guide
- Feature flags in development of GitLab
- Feature flags process
- Developing with feature flags
- Feature flag controls
- Document features deployed behind feature flags
- Frontend Development Guidelines
- Accessibility & Readability
- Ajax
- Architecture
- Axios
- Design Patterns
- Frontend Development Process
- DropLab
- Emojis
- Filter
- Frontend FAQ
- GraphQL
- Icons and SVG Illustrations
- InputSetter
- Performance
- Principles
- Security
- Tooling
- Vuex
- Vue
- Geo (development)
- Geo self-service framework (alpha)
- Gitaly developers guide
- GitLab development style guides
- API style guide
- Go standards and style guidelines
- GraphQL API style guide
- Guidelines for shell commands in the GitLab codebase
- HTML style guide
- JavaScript style guide
- Migration Style Guide
- Newlines style guide
- Python Development Guidelines
- SCSS style guide
- Shell scripting standards and style guidelines
- Sidekiq debugging
- Sidekiq Style Guide
- SQL Query Guidelines
- Vue.js style guide
- Instrumenting Ruby code
- Testing standards and style guidelines
- Flaky tests
- Frontend testing standards and style guidelines
- GitLab tests in the Continuous Integration (CI) context
- Review Apps
- Smoke Tests
- Testing best practices
- Testing levels
- Testing Rails migrations at GitLab
- Testing Rake tasks
- End-to-end Testing
- Beginner's guide to writing end-to-end tests
- End-to-end testing Best Practices
- Dynamic Element Validation
- Flows in GitLab QA
- Page objects in GitLab QA
- Resource class in GitLab QA
- Style guide for writing end-to-end tests
- Testing with feature flags
- Translate GitLab to your language
- Internationalization for GitLab
- Translating GitLab
- Proofread Translations
- Merging translations from CrowdIn
- Value Stream Analytics development guide
- GitLab subscription
- Activate GitLab EE with a license