PlanetScale のブランチ機能を試してみた

今回はサーバーレスRDBのブランチ機能を試してみました
2023.04.15

西田@CX事業本部です

今回はサーバーレスRDBの PlanetScale のブランチ機能を試してみました

ブランチ機能とは

Git のブランチのように、DBのスキーマのスナップショットを作成し、互いのブランチのスキーマに影響を出さずにを変更することができます

ブランチの種類

PlanetScale のブランチには2種類あります

ブランチ 説明
Development 開発用
Production 本番用

Production ブランチには Development ブランチに比べ以下の特徴があります

  • 高い可用性
  • プロダクションブランチのスキーマに対する直接的な変更を禁止し、デプロイリクエスト(後程説明)でのみ変更を可能とする(これはデフォルトの挙動で変更することは可能)
  • 日時でバックアップ

※ Production ブランチを2つ以上作成するには執筆時点だと Scaler Plan 以上にする必要があります

デプロイリクエストとは

GitHubのPull Requestに相当する機能で、スキーマの変更をリクエストすることができます。開発チームのメンバーがデプロイリクエストのスキーマの差分を確認し、レビューを行い、必要に応じてコメントし、問題がなくなればProductionブランチに適用するといった Purll Request を使った開発フローと同じようなフローでデータベースのスキーマを管理することができます

今回のシナリオ

今回は、前回のブログの 続きで、前回作成したお問合せフォームに「製品名」を新しく入力できるようにします。この「製品名」を保持するためのスキーマ変更をブランチ機能を使ってデプロイリクエストで実施します

main ブランチをProductionブランチに変更

最初に、デフォルトで作成されている main ブランチを Production ブランチに変更します

※ Production ブランチは main ブランチに限らず、どの Development ブランチからも Production ブランチにすること可能です。例えば stablerelease ブランチを作成し、それらを Production ブランチにすることができます

main ブランチの画面を開くと、Production ブランチが存在しないという警告と一緒に、Development ブランチを Production ブランチに変更するためのボタンが表示されるので、これをクリックします

ダイアログが表示されるので、 main ブランチを選択し、「Promote Branch」ボタンをクリックします

Development ブランチを作成

デプロイリクエストの元ブランチになる Developmentブランチを作成します。こちらのブランチに対しスキーマ変更を行い、機能追加が完了すれば、Productionブランチにデプロイリクエストを作成します

名前に今回の変更内容を表した名前を入力しブランチを作成します。元になるブランチはプロダクションブランチにした mainを選択し、mainブランチと同じ、東京リージョンを選択します

アプリケーションを修正

新しく作ったDevelopmentブランチにローカル環境から接続します

$ pscale connect test-branch add-product-name --port 3309

前回の続きなので .envファイルに以下の環境変数が設定されてる前提です。上記の接続を利用して Next.js が Development ブランチのデータベースに繋がることを想定してます

DATABASE_URL="mysql://root@127.0.0.1:3309/test-branch"

schema/schema.prismaファイルに必要な変更を加えます。今回は製品名を追加するので、 productNameというフィールドを追加します

@@ -16,4 +16,5 @@ model Inquiry {
   email String
   subject String
   message String
+  productName String
 }

Schemaの変更を PlanetScale のテーブル定義に反映します

npx prisma db push

PlanetScale のブランチの Schema タブで変更が反映されてることを確認します

※ ブランチ作成時にデータはコピーされないので、注意してください。データもコピーしたい場合は Team Plan 以上から利用できる Data Branching 機能が必要です

この時点で Production ブランチを確認すると、Development ブランチに対するスキーマ変更の影響がないことが確認できました

諸々修正し、製品名を登録できるようにします

参考までに、前回 からの差分を載せておきます

diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index d89ef35..8ccab32 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -16,4 +16,5 @@ model Inquiry {
   email String
   subject String
   message String
+  productName String
 }
\ No newline at end of file
diff --git a/src/libs/database.ts b/src/libs/database.ts
index 350eac5..12a5646 100644
--- a/src/libs/database.ts
+++ b/src/libs/database.ts
@@ -6,6 +6,7 @@ export type InquiryInput = {
     name: string,
     email: string,
     subject: string,
+    productName: string,
     message: string,
 }
 
diff --git a/src/pages/api/inquiries/index.ts b/src/pages/api/inquiries/index.ts
index 0f8e733..68fa241 100644
--- a/src/pages/api/inquiries/index.ts
+++ b/src/pages/api/inquiries/index.ts
@@ -9,6 +9,7 @@ const postInquiry = async (req: NextApiRequest, res: NextApiResponse) => {
         name: body.name,
         email: body.email,
         subject: body.subject,
+        productName: body.productName,
         message: body.message,
     })
 
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 483e0c9..5f2130c 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -12,12 +12,14 @@ const Home: NextPage = () => {
   const [name, setName] = useState("");
   const [email, setEmail] = useState("");
   const [subject, setSubject] = useState("");
+  const [productName, setProductName] = useState("");
   const [message, setMessage] = useState("");
 
   const clearForm = () => {
     setName("");
     setEmail("");
     setSubject("");
+    setProductName("");
     setMessage("");
   }
 
@@ -27,7 +29,7 @@ const Home: NextPage = () => {
     e.preventDefault();
 
     const response = await fetch("/api/inquiries", {method: "POST", body: JSON.stringify({
-      name, email, subject, message
+      name, email, subject, productName, message
     }),
     headers: {
       "Content-Type": "application/json"
@@ -61,6 +63,12 @@ const Home: NextPage = () => {
           </label>
           <input onChange={(e) => setSubject(e.target.value)} type="text" value={subject} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700" />
         </div>
+        <div className="mb-4">
+          <label className="block text-gray-700 text-sm font-bold mb-1">
+            ProductName
+          </label>
+          <input onChange={(e) => setProductName(e.target.value)} type="text" value={productName} className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700" />
+        </div>
         <div className="mb-4">
           <label className="block text-gray-700 text-sm font-bold mb-1">
             Message
@@ -78,6 +86,7 @@ const Home: NextPage = () => {
             <th className="py-3 px-6">Email</th>
             <th className="py-3 px-6">Subject</th>
             <th className="py-3 px-6">Message</th>
+            <th className="py-3 px-6">ProductName</th>
           </tr>
         </thead>
         <tbody>
@@ -87,6 +96,7 @@ const Home: NextPage = () => {
                 <td className="py-4 px-6">{inquiry.name}</td>
                 <td className="py-4 px-6">{inquiry.email}</td>
                 <td className="py-4 px-6">{inquiry.subject}</td>
+                <td className="py-4 px-6">{inquiry.productName}</td>
                 <td className="py-4 px-6">{inquiry.message}</td>
               </tr>
             )

デプロイリクエストを作成

PlanetScale のブランチ画面からデプロイリクエストを作成します

デプロイリクエストの画面で変更されたスキーマの差分が確認できます

コメントや、Approveもできます

デプロイリクエストをデプロイする

内容を確認できたら、デプロイリクエストをデプロイし、Production ブランチにスキーマ変更を反映します

執筆時にここで、追加された列にデフォルト値が設定されてないことでデプロイエラーが発生しました。 productName にデフォルト値を設定して再度デプロイします。すでにデータが登録されているテーブルに列追加する際は、デフォルト値を設定するか、Not Null 制約を外す必要がありそうです

prisma/schema.prisma

productName String @default("") // デフォルト値を追加

変更をDevelopmentブランチに反映

npx prisma db push

エラーになったデプロイリクエストを完了させ、新しくデプロイリクエストを作成し、main ブランチにデプロイします

大体30秒ほどで完了しました

Vercel にアプリケーションの変更をデプロイ

最後にVercel にアプリケーションの変更をデプロイします

git push origin main

執筆時にprisma の client が再生成されていないことが原因で Vercel のデプロイが失敗してしまったのでpackage.json の postinstallに prisma の client を再生成するコマンドを追加します

package.json

{
	"scripts": {
    //... 省略
    "postinstall": "prisma generate"
  }
}

再度デプロイして変更が反映されてることを確認します

まとめ

今回は PlanetScale のブランチ機能を試してみました。ポピュラーな開発フローである Pull Request に似た感じで開発を進めていけるブランチ機能は、開発者にとって馴染みがあるので、使ってみると便利なのではないでしょうか

この記事が誰かの役に立てば幸いです