Closed10

Remix+CloudflareでWebサイトを作る 23(既存のテーブルにカラム追加・初期描画が遅すぎるけど原因わからないから作り直してみる・Bye, Joy UI)

saneatsusaneatsu

【2024-04-07】Prismaでデータが入っている既存のテーブルにカラムを追加する

We need to reset the SQLite database All data will be lost.

https://www.prisma.io/docs/orm/prisma-migrate/workflows/customizing-migrations

まずは既存のDBの定義とPrismaのモデルと同期する。

schema.prisma
- includeRelationFromFields = true
+ includeRelationFromFields = "true"

"true"という文字列なんか嫌だな...。

--create-onlyオプションをつけてマイグレーションファイルの作成だけをして適用はしないようにする。

$ npx prisma migrate dev --create-only --name your-migration-name
Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": SQLite database "dev.db" at "file:./dev.db"

- Drift detected: Your database schema is not in sync with your migration history.
✖ We need to reset the SQLite database "dev.db" at "file:./dev.db"
Do you want to continue? All data will be lost. … no

All data will be lost. ....???
--create-onlyするだけでなぜ消えるのか。

Prisma migrationを導入しようとすると、最初のタイミングでmigration用の管理テーブルが作られる。そして手動で定義していたものについてはその時点で既存のテーブルは一度削除されてしまうという仕様のようだ。テーブルが削除されるのでもちろんデータも消える。開発環境とは言えそれなりの量のデータが入っていること、本番適用時にデータが全部消えてしまうのは論外なこともあり色々と調べたり試したりしたものの良い解決策は見つからなかった。

--create-onlyをつけて実行すると実行日時のフォルダとともにマイグレーションファイルができあがり、マイグレーションの管理テーブルだけが作成された状態でマイグレーションファイルの内容は反映されていない状態となる。大事なことなので2回言うが、DB上は全部のテーブルが削除された上でマイグレーションの管理テーブルのみが存在しているという状態になる。

これに関してはどうしようもないので既存のものに導入する場合、かつデータも残したい場合は事前にバックアップするなり、テーブル単位でエクスポートしておくことは必須といえる。

ref: 既存のデータベースでPrisma migrateする。しかもPostGISを使う - Sweet Escape

ALTER TABLE tableName ADD COLUMN columnName BOOLEAN DEFAULT FALSE;

こんな感じでカラムだけ追加するSQL作成してほしいんだけどどこらへんがネックなんだろうか。

Refs

time-travelでバックアップ

マイグレーションファイル適当に作って中身のSQL書き換えようかなとも思ったけど、Prismaの思想に反するのもなんか良くない気もする。

ということで「バックアップ取って→マイグレーション→データ入れ直す」の一連の流れをちゃんとわかっておこう。

Backups (Legacy) · Cloudflare D1 docs

wrangler d1 backup コマンドでバックアップできるっぽいけど、開発環境でのバックアップコマンドはなさそう。

Commands - Wrangler · Cloudflare Workers docs

wrangerコマンドのページを見ても--local的なオプションがないのでなさそう。

Cloudflare Workers から D1 を操作する

今はwrangler d1 time-travelの方か。そうでした。

結論

Development

カラムを追加するとDBのデータはすべて消えてしまうので諦める。

Staging/Production

以下でなんとかなりそうだけど全然スマートじゃない気がする。

bookmark取得
→テーブル初期化
$ npm exec -- zx prisma/d1-migration.mjs --env=stagingでスキーマ更新
→restore

schema.prisma に変更があった場合だけこの処理やってくれるGitHub Actionsとかあったら便利かな?

saneatsusaneatsu

UserテーブルにOptionalなカラムであるtestを追加した場合はAlterだけ

-- AlterTable
ALTER TABLE "User" ADD COLUMN "test" TEXT;

UserテーブルにRequiredなカラムであるhogeを追加した場合は、tempとなるテーブルを作成してデータを移し替えて元のテーブルを削除して、tempとなるテーブルをリネームしている。

/*
  Warnings:

  - Added the required column `hoge` to the `User` table without a default value. This is not possible if the table is not empty.

*/
-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_User" (
    "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    "name" TEXT,
    "test" TEXT,
    "hoge" TEXT NOT NULL,
    "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "updatedAt" DATETIME NOT NULL
);
INSERT INTO "new_User" ("createdAt", "email", "iconUrl", "id", "isArchived", "name", "role", "test", "updatedAt") SELECT "createdAt", "email", "iconUrl", "id", "isArchived", "name", "role", "test", "updatedAt" FROM "User";
DROP TABLE "User";
ALTER TABLE "new_User" RENAME TO "User";
CREATE UNIQUE INDEX "User_id_key" ON "User"("id");
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;
saneatsusaneatsu

【2024-04-10】Service Bindingのためにmonorepo構成にする

背景

https://zenn.dev/chimame/articles/f86db24897be6a

こちらを参考にmonorepo構成にしてみている。

https://zenn.dev/moutend/articles/f9409d43724da5
Service Binding自体についてこっちの記事も良かった。

エラー

Remix側とPrismaのWorkerがうまく連携できていないっぽい。

WARNING: Tried to access method or property 'fetchIdByEmail' on a Service Binding or Durable Object stub. Are you trying to use RPC? If so, please enable the 'rpc' compat flag or update your compat date to 2024-04-03 or later (see https://developers.cloudflare.com/workers/configuration/compatibility-dates/ ). If you are not trying to use RPC, please note that in the future, this property (and all other property names) will appear to be present as an RPC method.

https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/rpc/

指示通りにしても治らず。

wrangler.toml
compatibility_date = "2024-04-03"
saneatsusaneatsu

【2024-05-12】Joy UIを使っていると発生するエラー:Hydration failed because the initial UI does not match what was rendered on the server.

背景

アプリケーションの初期描画が異常に遅くて原因を特定できないのでpnpm+monorepo構成で作り治してみている。
その中でJoy UIを使いながら以下エラーに遭遇。

エラー内容

Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server.
Uncaught Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.

解決

https://zenn.dev/akkey247/articles/20240417_remix_environment_construction_3#4.-ハイドレーションエラーを解消する

@emotion/cache を追加して以下のようにする。

app/entry.client.tsx
import { RemixBrowser } from "@remix-run/react";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
+ import { CacheProvider } from '@emotion/react';
+ import createCache from '@emotion/cache';

+ function createEmotionCache() {
+   return createCache({ key: 'css' });
+ }

+ const cache = createEmotionCache();

startTransition(() => {
  hydrateRoot(
    document,
    <StrictMode>
+       <CacheProvider value={cache}>
        <RemixBrowser />
+       </CacheProvider>
    </StrictMode>
  );
});
saneatsusaneatsu

【2024-05-12】Warning: Extra attributes from the server: cz-shortcut-listen

コンソールで出てるこれなんだ?

https://zenn.dev/popcorn/scraps/2b1598d42367f8

ここによるとChromeの拡張機能によるものらしい。
確かにシークレットブラウザで立ち上げると表示されないし無視する。

https://zenn.dev/tk_c/articles/26bbd0afffdbb7

ここには<html lang="ja" suppressHydrationWarning={true}> という感じでsuppressHydrationWarning={true} を追加すると解消できると書いてあるけどできなかった。

saneatsusaneatsu

ありがとうございます!

アプデしてみたんですが直りませんでした...!😔

saneatsusaneatsu

https://zenn.dev/link/comments/eedf10472db184

ここみたいにバンドルサイズ見ようかと思ったらエラー

$ npx wrangler deploy 'functions/[[path]].ts' --outdir bundled/ --dry-run
 ⛅️ wrangler 3.55.0
-------------------
▲ [WARNING] The entrypoint functions/[[path]].ts has exports like an ES Module, but hasn't defined a default export like a module worker normally would. Building the worker using "service-worker" format...

✘ [ERROR] Top-level await is currently not supported with the "iife" output format

    node_modules/.pnpm/@prisma+client@5.13.0_prisma@5.13.0/node_modules/.prisma/client/wasm-worker-loader.js:1:16:
      1 │ export default (await import('./query_engine_bg.wasm')).default
        ╵                 ~~~~~

https://zenn.dev/link/comments/782d77c97947c3

前にも遭遇してScrap書いてた。無視するか〜。

saneatsusaneatsu

【2024-05-12】初期描画が遅い原因を探る

耐えうる速度で動いてそう?

アプリの初期描画が異常に遅すぎるが原因がわからないから試しに始めから作ってみてるけどスコアは問題無い。

ただし、以下のようなメッセージが出てる。。

API連携しているページだと「最初のサーバーの応答時間」が更にかかる

API連携しているページで計測(by Lighthouse)を行うと更に遅い。
もう1回実行したら900msもかかっていた。

サーバーの応答時間は何ms以下だといいのか?

サーバーの応答時間を短縮する  |  Lighthouse  |  Chrome for Developers には以下のように書いてある。

サーバーがメインのドキュメント リクエストに応答するまでに 600 ミリ秒以上待機すると、この監査は失敗します。ユーザーは、ページの読み込み時間が長いことを嫌います。サーバーの応答時間が遅いことは、ページの読み込みが長い原因の 1 つと考えられます。

まずは600ms以内を目指す。

原因は?

API叩いているコードは一覧取得しているだけのシンプルなものだし、API連携してなくても時間かかってることを見るとAPI連携は直接な原因じゃないはず。

Cloudflare PagesにアップロードしただけだしCloudflare側にも原因はないはず。

Joy UIを消してみる

特段怪しいとは思っていないが他に候補がないので一旦Joy UIを削除してみる。
複数回計測したものは結果をすべて書いた。

ページ 削除前(ms) 削除後(ms)
API連携無しのページ 620 80, 220, 260, 270, 290
API連携有りのページ 720, 900 220, 230, 400, 690, 770, 840, 950, 1130

結論、Joy UIは最初のサーバーの応答時間の律速になっていて削除したら早くなった。
段階的に少しずつ各コンポーネントからJoy UIを剥いでいったんだけど削除すればするほど早くなった。

「API連携有りのページ」の方は220msと早くなっている一方、690/770/840/1130msとかいう数字出てる。幅ありすぎじゃないか?

APIを叩かない場合何回試してみても200台だった。
連続して測定したら200台なのに、5分くらい時間おいたら800とか900超えるのでCloudflare側のキャッシュの問題な気がする。

結論

  • Joy UIを削ると最初のサーバーの応答時間が早くなった
    • Joy UIのコンポーネントを削れば削るほど早くなった
    • 具体的には約400msほど早くなった
  • Cloudflare側のキャッシュ次第でもっとUXを向上できそう

Next Action

1. Cloudflareのキャッシュの設定をいじって再度測定してみる

https://zenn.dev/ebifran/articles/c216cae50bede6

ここを参考にResponse Headerを見てみると Cf-Cache-StatusDYNAMICになっていた。
つまり、キャッシュを利用せずに動的にコンテンツを生成しているということ。
HITMISSになっているとキャッシュが利用されているということになるっぽい。

まずはここらへんの設定を変えてみる。
そうすれば「API連携有りのページ」の場合でも200ms台に収まるのでは?

2. UIコンポーネントを変えて再度測定してみる

Joy UI(MUI)を使わず他のコンポーネントを使う形式で書き直して再度速度を測定する(めんどくさ〜)。

第一候補はshadcn + Tailwind。
AutoCompleteのコンポーネントが無いのがちょっとつらいけどIssueは10個くらい立ってる。

https://github.com/shadcn-ui/ui/issues/66

このIssueに代替案が結構載っているのでどうにでもなりそう。

反省

描画遅いな〜という違和感があったけど「後でやればいいや」と先延ばしにしてきた結果、再度ゼロから作り直して原因を特定する、今まで使っていたUIコンポーネントを手放す、という大ダメージを負っている。

面倒くさがってやらなかったことをあと伸ばしにしたら、もっと面倒になるということがまだわからんのか??

このスクラップは2024/05/12にクローズされました