自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Sentry 開發(fā)者貢獻(xiàn)指南-數(shù)據(jù)庫遷移

運維 數(shù)據(jù)庫運維
重命名列是危險的,會導(dǎo)致停機。發(fā)生這種情況的原因是在部署期間將運行舊/新代碼的混合。因此,一旦我們在 Postgres 中重命名該列,如果舊代碼嘗試訪問它,它就會立即開始出錯。

Django 遷移是我們處理 Sentry 中數(shù)據(jù)庫更改的方式。

Django 遷移官方文檔:https://docs.djangoproject.com/en/2.2/topics/migrations/。

這些將涵蓋了解遷移正在執(zhí)行的操作所需的大部分內(nèi)容。

命令

請注意,對于所有這些命令,如果在 getsentry 存儲庫中,您可以將 getsentry 替換為 sentry。

將您的數(shù)據(jù)庫升級到最新

sentry upgrade 會自動更新你的遷移。您也可以運行 sentry django migrate 來直接訪問遷移命令。

將您的數(shù)據(jù)庫移動到特定的遷移

當(dāng)您要測試遷移時,這會很有幫助。

sentry django migrate - 請注意,migration_name 可以是部分匹配,通常數(shù)字就是你所需要的。

例如:sentry django migrate sentry 0005

這也可用于回滾遷移。如果你犯了錯誤,在開發(fā)中很有用。

為遷移生成 SQL

這對審查您的代碼的人很有幫助,因為并不總是清楚 Django 遷移實際要做什么。

sentry django sqlmigrate

例如 sentry django sqlmigrate sentry 0003

生成遷移

這會根據(jù)您對模型所做的更改自動為您生成遷移。

sentry django makemigrations

或者

sentry django makemigrations 用于一個指定的 app。

例如 sentry django makemigrations sentry

當(dāng)您在 pr 中包含遷移時,還要為遷移生成 sql 并將其作為注釋包含在內(nèi),以便您的審閱者可以更輕松地了解 Django 正在做什么。

您還可以使用 sentry django makemigrations --empty 生成空遷移。這對于數(shù)據(jù)遷移和其他自定義工作很有用。

將遷移合并到 master

合并到 master 時,您可能會注意到與 migrations_lockfile.txt 的沖突。這個文件是為了幫助我們避免將具有相同遷移編號的兩個遷移合并到 master,如果您與它發(fā)生沖突,那么很可能有人在您之前提交了遷移。

指南

在運行遷移時,我們需要注意一些事項。

過濾器

如果(數(shù)據(jù))遷移涉及大表或未索引的列,最好迭代整個表而不是使用 filter。例如:

  1. EnvironmentProject.objects.filter(environment__name="none"

因為 EnvironmentProject 行太多,這會一次將太多行帶入內(nèi)存。相反,我們應(yīng)該使用 RangeQuerySetWrapperWithProgressBar 遍歷所有 EnvironmentProject 行,因為它會分塊進(jìn)行。例如:

  1. for env in RangeQuerySetWrapperWithProgressBar(EnvironmentProject.objects.all()): 
  2.     if env.name == 'none'
  3.         # Do what you need 

我們通常更喜歡避免將 .filter 與 RangeQuerySetWrapperWithProgressBar 一起使用。由于它已經(jīng)通過 id 對表進(jìn)行排序,因此我們無法利用字段上的任何索引,并且可能會為每個塊掃描大量行。這會運行得更慢,但我們通常更喜歡這樣,因為它在更長的時間內(nèi)平均負(fù)載,并使每個查詢獲取每個塊的成本相當(dāng)?shù)汀?/p>

索引

我們更喜歡使用 CREATE INDEX CONCURRENTLY 在現(xiàn)有的大型表上創(chuàng)建索引。當(dāng)我們這樣做時,我們無法在事務(wù)中運行遷移,因此使用 atomic = False 來運行這些很重要。

刪除列/表

由于我們的部署過程,這很復(fù)雜。當(dāng)我們部署時,我們運行遷移,然后推出應(yīng)用程序代碼,這需要一段時間。這意味著如果我們只是刪除一個列或模型,那么 sentry 中的代碼將查找這些列/表并在部署完成之前出錯。在某些情況下,這可能意味著 Sentry 在部署完成之前很難停機。

為避免這種情況,請執(zhí)行以下步驟:

  • 如果列不是空的,則將其標(biāo)記為空,并創(chuàng)建一個遷移。
  • 部署。
  • 從模型中刪除列,但在遷移中確保我們只將狀態(tài)標(biāo)記為已刪除(removed)。
  • 部署。
  • 最后,創(chuàng)建一個刪除列的遷移。

這是刪除已經(jīng)可以為空的列的示例。首先我們從模型中刪除列,然后修改遷移以僅更新狀態(tài)而不進(jìn)行數(shù)據(jù)庫操作。

  1. operations = [ 
  2.         migrations.SeparateDatabaseAndState( 
  3.             database_operations=[], 
  4.             state_operations=[ 
  5.                 migrations.RemoveField(model_name="alertrule"name="alert_threshold"), 
  6.                 migrations.RemoveField(model_name="alertrule"name="resolve_threshold"), 
  7.                 migrations.RemoveField(model_name="alertrule"name="threshold_type"), 
  8.             ], 
  9.         ) 
  10.     ] 

一旦部署完成,我們就可以部署實際的列刪除。這個 pr 只會有一個遷移,因為 Django 不再知道這些字段。請注意,反向 SQL 僅適用于開發(fā)人員,因此可以不分配默認(rèn)值或進(jìn)行任何類型的回填:

  1. operations = [ 
  2.         migrations.SeparateDatabaseAndState( 
  3.             database_operations=[ 
  4.                 migrations.RunSQL( 
  5.                     ""
  6.                     ALTER TABLE "sentry_alertrule" DROP COLUMN "alert_threshold"
  7.                     ALTER TABLE "sentry_alertrule" DROP COLUMN "resolve_threshold"
  8.                     ALTER TABLE "sentry_alertrule" DROP COLUMN "threshold_type"
  9.                     """, 
  10.                     reverse_sql=""
  11.                     ALTER TABLE "sentry_alertrule" ADD COLUMN "alert_threshold" smallint NULL
  12.                     ALTER TABLE "sentry_alertrule" ADD COLUMN "resolve_threshold" int NULL
  13.                     ALTER TABLE "sentry_alertrule" ADD COLUMN "threshold_type" int NULL
  14.  
  15.                     """, 
  16.                 ) 
  17.             ], 
  18.             state_operations=[], 
  19.         ) 
  20.     ] 

如果該表在其他表中被引用為外鍵,則需要格外小心。在這種情況下,首先刪除其他表中的外鍵列,然后返回到此步驟。

  • 通過在列上設(shè)置 db_constraint=False,刪除此表到其他表的任何數(shù)據(jù)庫級外鍵約束。
  • 部署
  • 從 sentry 代碼庫中刪除模型和所有引用。確保遷移僅將狀態(tài)標(biāo)記為已刪除。
  • 部署。
  • 創(chuàng)建一個刪除表的遷移。
  • 部署

這是刪除此模型的示例:

  1. class AlertRuleTriggerAction(Model): 
  2.     alert_rule_trigger = FlexibleForeignKey("sentry.AlertRuleTrigger"
  3.     integration = FlexibleForeignKey("sentry.Integration"null=True
  4.     type = models.SmallIntegerField() 
  5.     target_type = models.SmallIntegerField() 
  6.     # Identifier used to perform the action on a given target 
  7.     target_identifier = models.TextField(null=True
  8.     # Human readable name to display in the UI 
  9.     target_display = models.TextField(null=True
  10.     date_added = models.DateTimeField(default=timezone.now) 
  11.  
  12.     class Meta: 
  13.         app_label = "sentry" 
  14.         db_table = "sentry_alertruletriggeraction" 

首先,我們檢查了它沒有被任何其他模型引用,它沒有。接下來,我們需要刪除和 db 級外鍵約束。為此,我們改變這兩列并生成一個遷移:

  1. alert_rule_trigger = FlexibleForeignKey("sentry.AlertRuleTrigger", db_constraint=False
  2. integration = FlexibleForeignKey("sentry.Integration"null=True, db_constraint=False

遷移中的操作看起來像

  1. operations = [ 
  2.        migrations.AlterField( 
  3.            model_name='alertruletriggeraction'
  4.            name='alert_rule_trigger'
  5.            field=sentry.db.models.fields.foreignkey.FlexibleForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADEto='sentry.AlertRuleTrigger'), 
  6.        ), 
  7.        migrations.AlterField( 
  8.            model_name='alertruletriggeraction'
  9.            name='integration'
  10.            field=sentry.db.models.fields.foreignkey.FlexibleForeignKey(db_constraint=Falsenull=True, on_delete=django.db.models.deletion.CASCADEto='sentry.Integration'), 
  11.        ), 
  12.    ] 

我們可以看到它生成的 sql 只是刪除了 FK 約束

  1. BEGIN
  2. SET CONSTRAINTS "a875987ae7debe6be88869cb2eebcdc5" IMMEDIATE; ALTER TABLE "sentry_alertruletriggeraction" DROP CONSTRAINT "a875987ae7debe6be88869cb2eebcdc5"
  3. SET CONSTRAINTS "sentry_integration_id_14286d876e86361c_fk_sentry_integration_id" IMMEDIATE; ALTER TABLE "sentry_alertruletriggeraction" DROP CONSTRAINT "sentry_integration_id_14286d876e86361c_fk_sentry_integration_id"
  4. COMMIT

所以現(xiàn)在我們部署它并進(jìn)入下一階段。

下一階段涉及從代碼庫中刪除對模型的所有引用。所以我們這樣做,然后我們生成一個遷移,從遷移狀態(tài)中刪除模型,而不是數(shù)據(jù)庫。此遷移中的操作如下所示

  1. operations = [ 
  2.         migrations.SeparateDatabaseAndState( 
  3.             state_operations=[migrations.DeleteModel(name="AlertRuleTriggerAction")], 
  4.             database_operations=[], 
  5.         ) 
  6.     ] 

并且生成的 SQL 顯示沒有發(fā)生數(shù)據(jù)庫更改。所以現(xiàn)在我們部署它并進(jìn)入最后一步。

在這最后一步中,我們只想手動編寫 DDL 來刪除表。所以我們使用 sentry django makemigrations --empty 來產(chǎn)生一個空的遷移,然后修改操作如下:

  1. operations = [ 
  2.         migrations.RunSQL( 
  3.             ""
  4.             DROP TABLE "sentry_alertruletriggeraction"
  5.             """, 
  6.             reverse_sql="CREATE TABLE sentry_alertruletriggeraction (fake_col int)", # We just create a fake table here so that the DROP will work if we roll back the migration. 
  7.         ) 
  8.     ] 

然后我們部署它,我們就完成了。

外鍵

創(chuàng)建外鍵大多沒問題,但是對于像 Project、Group 這樣的大/繁忙的表,由于獲取鎖的困難,它可能會導(dǎo)致問題。您仍然可以創(chuàng)建 Django 級別的外鍵,而無需創(chuàng)建數(shù)據(jù)庫約束。為此,請在定義鍵時設(shè)置 db_constraint=False。

重命名表

重命名表很危險,會導(dǎo)致停機。發(fā)生這種情況的原因是在部署期間將運行舊/新代碼的混合。因此,一旦我們在 Postgres 中重命名該表,如果舊代碼嘗試訪問它,它就會立即開始出錯。有兩種方法可以處理重命名表:

  • 不要在 Postgres 中重命名表。相反,只需在 Django 中重命名模型,并確保將 Meta.db_table 設(shè)置為當(dāng)前表名,這樣不會有任何中斷。這是首選方法。
  • 如果你真的想重命名表,那么步驟將是:
  • 使用新名稱創(chuàng)建一個表
  • 開始對舊表和新表進(jìn)行雙重寫入,最好是在事務(wù)中。
  • 將舊行回填到新表中。
  • 將 model 更改為從新表開始讀取。
  • 停止寫入舊表并從代碼中刪除引用。
  • 丟棄舊表。
  • 一般來說,這是不值得做的,與回報相比,這需要冒很多風(fēng)險/付出很多努力。

添加列

創(chuàng)建新列時,它們應(yīng)始終創(chuàng)建為可為空的。這是出于兩個原因:

  • 如果存在現(xiàn)有行,添加非空列需要設(shè)置默認(rèn)值,添加默認(rèn)值需要完全重寫表。這是危險的,很可能會導(dǎo)致停機
  • 在部署期間,新舊代碼混合運行。如果舊代碼嘗試向表中插入一行,則插入將失敗,因為舊代碼不知道新列存在,因此無法為該列提供值。

向列添加 NOT NULL

將 not null 添加到列可能很危險,即使該列的表的每一行都有數(shù)據(jù)。這是因為 Postgres 仍然需要對所有行執(zhí)行非空檢查,然后才能添加約束。在小表上這可能沒問題,因為檢查會很快,但在大表上這可能會導(dǎo)致停機。這里有幾個選項可以確保安全:

  • ALTER TABLE tbl ADD CONSTRAINT cnstr CHECK (col IS NOT NULL) NOT VALID; ALTER TABLE tbl VALIDATE CONSTRAINT cnstr;. 首先,我們將約束創(chuàng)建為無效。然后我們之后驗證它。我們?nèi)匀恍枰獟呙枵麄€表來驗證,但我們只需要持有一個 SHARE UPDATE EXCLUSIVE 鎖,它只會阻止其他 ALTER TABLE 命令,但允許讀/寫繼續(xù)。這很有效,但會有 0.5-1% 的輕微性能損失。在 Postgres 12 之后,我們可以擴(kuò)展這個方法來添加一個真正的 NOT NULL 約束。
  • 如果表足夠小并且體積足夠小,那么創(chuàng)建一個普通的 NOT NULL 約束應(yīng)該是安全的。小是幾百萬行或更少。

添加具有默認(rèn)值的列

向現(xiàn)有表添加具有默認(rèn)值的列是危險的。這需要 Postgres 鎖定表并重寫它。相反,更好的選擇是:

在 Postgres 中添加沒有默認(rèn)值的列,但在 Django 中添加默認(rèn)值。這使我們能夠確保所有新行都具有默認(rèn)值。這是通過修改遷移文件以包含 migrations.SeperateDatabaseAndState 來完成的

  1. operations = [ 
  2.     migrations.SeparateDatabaseAndState( 
  3.         database_operations=[ 
  4.             migrations.AddField( 
  5.                 model_name="mymodel"
  6.                 name="new_field"
  7.                 # Don't use a default in Postgres, a data migration can be used afterward to backfill 
  8.                 field=models.PositiveSmallIntegerField(null=True), 
  9.             ), 
  10.         ], 
  11.         state_operations=[ 
  12.             migrations.AddField( 
  13.                 model_name="mymodel"
  14.                 name="new_field"
  15.                 # Use the default in Django, new rows will use the specified default 
  16.                 field=models.PositiveSmallIntegerField(null=Truedefault=1), 
  17.             ), 
  18.         ], 
  19.     ) 
  20.     ] 
  • 通過數(shù)據(jù)遷移使用默認(rèn)值回填預(yù)先存在的行。

改變列類型

改變列的類型通常是危險的,因為它需要重寫整個表。有一些例外:

  • 將 varchar() 更改為更大尺寸的 varchar。
  • 將任何 varchar 更改為 text
  • 將 numeric 更改為 numeric,其中 precision 更高但 scale 相同。

對于任何其他類型,最好的前進(jìn)路徑通常是:

  • 創(chuàng)建具有新類型的列。
  • 開始對新舊列進(jìn)行雙重寫入。
  • 回填并將舊列值轉(zhuǎn)換為新列。
  • 更改代碼以使用新字段。
  • 停止寫入舊列并從代碼中刪除引用。
  • 從數(shù)據(jù)庫中刪除舊列。
  • 通常,這值得在 #discuss-backend 中討論。

重命名列

重命名列是危險的,會導(dǎo)致停機。發(fā)生這種情況的原因是在部署期間將運行舊/新代碼的混合。因此,一旦我們在 Postgres 中重命名該列,如果舊代碼嘗試訪問它,它就會立即開始出錯。有兩種方法可以處理重命名列:

  • 不要重命名 Postgres 中的列。相反,只需在 Django 中重命名字段,并在定義中使用 db_column 將其設(shè)置為現(xiàn)有的列名,這樣就不會中斷。這是首選方法。
  • 如果你真的想重命名列,那么步驟將是:
    • 創(chuàng)建具有新名稱的列
    • 開始對新舊列進(jìn)行雙重寫入。
    • 將舊列值回填到新列中。
    • 將字段更改為從新列開始讀取。
    • 停止寫入舊列并從代碼中刪除引用。
    • 從數(shù)據(jù)庫中刪除舊列。

一般來說,這是不值得做的,與回報相比,這需要冒很多風(fēng)險/付出很多努力。

 

責(zé)任編輯:武曉燕 來源: 黑客下午茶
相關(guān)推薦

2022-01-11 20:42:54

開發(fā)Sentry標(biāo)志

2022-01-17 19:34:43

SentryWeb APISentry API

2022-01-15 23:33:47

SentryPyCharm配置

2022-01-18 23:26:45

開發(fā)

2022-01-02 23:26:08

開發(fā)SDK Sentry

2022-01-03 22:59:30

開發(fā)SDK數(shù)據(jù)

2021-12-15 20:06:48

ReactJSSentry開發(fā)者

2021-12-25 22:31:55

Sentry 監(jiān)控SDK 開發(fā) 性能監(jiān)控

2022-01-21 21:33:03

開發(fā)JavaScript應(yīng)用

2022-01-13 20:13:31

元宇宙搜索引擎

2021-12-31 18:35:40

監(jiān)控Sentry開發(fā)

2022-01-02 06:59:43

SentrySDK 開發(fā)客戶端報告

2022-01-19 19:49:53

Sentry瀏覽器SDK

2021-12-16 20:12:37

后端開發(fā)Sentry

2022-01-20 19:49:10

Sentry開發(fā)Scope

2021-12-17 19:15:51

前端蟲洞狀態(tài)

2017-04-01 18:00:08

開發(fā)者數(shù)據(jù)庫

2017-11-23 15:06:14

前端數(shù)據(jù)庫開發(fā)

2011-03-16 09:33:45

數(shù)據(jù)庫開發(fā)錯誤

2011-03-16 09:38:05

點贊
收藏

51CTO技術(shù)棧公眾號