Upgrade to Pro — share decks privately, control downloads, hide ads and more …

TimeTree の Spanner 事例 紹介 ~ DynamoDB からの移行と全文検索 ~

TimeTree の Spanner 事例 紹介 ~ DynamoDB からの移行と全文検索 ~

Google Cloud Next Tokyo 2026での登壇資料
DynamoDBからSpannerへの移行とSpannerの全文検索に関する内容

Avatar for TimeTree, Inc.

TimeTree, Inc.

July 31, 2026

More Decks by TimeTree, Inc.

Other Decks in Technology

Transcript

  1. コア機能 Key Features <公式>TimeTree最新… TimeTreeのアップデート情報 や、 新機能のリリーススケジュールを お届けする公式カ … <公式>エブリーモール

    EveryMallのセールや限定クー ポン配布日をお知らせする公式 カレンダーです!毎 … サザンスターズ公式ス … プロ野球チーム「サザンスター ズ」の公式カレンダーです。試合 日程やホームゲームの … クリスタル・サーガ ⚔💎✨大人気ファンタジー作品 「クリスタル・サーガ」のアニメ放 送スケジュールや … ※ その他機能の詳細については HP をご覧ください Google Cloud Next Tokyo 共有カレンダー 公開カレンダー TimeTree の基本となるカレンダーシェア。 カレンダー形式でだれでも気軽にイベント情報を発信できるプラッ 家族、恋人、同僚など多彩なシーンでだれでも トフォーム。発信者とカレンダーユーザーを予定で 簡単に予定共有・管理ができます。 繋ぐ新しいコミュニケーションです。 Proprietary
  2. AI 機能 AI Features ※ その他機能の詳細については HP をご覧ください Google Cloud

    Next Tokyo 予定スキャン ラボ 音声入力 ラボ 学校のプリント、イベントのポスター、メールのスクリーン 一度に複数の予定を手軽に音声登録できる機能。TimeTree ラ ショットなど、様々な画像から情報を自動抽出し、手間なく予 ボは新機能をいち早く体験できる実験場です。 定に登録できる機能 Proprietary
  3. サービス規模 ユーザー数 7,500 万 ↗ 予定数 160 億 ↗ 対応言語数

    13 言語 ↗ 登録ユーザー数が 7,500万 を突破、 その比率はおおよそ50:50 ( 国内:海外 ) 作成された予定数が160 億件を突破、 プライベートからビジネスまで世界中の人々の 日常的なスケジュール管理を支持 グローバルにサービスを展開、 200 万ユーザーを突破している国も多数 Google Cloud Next Tokyo Proprietary
  4. 技術スタック ( 一部抜粋 ) Infrastructure IaC Google Cloud, AWS (

    DynamoDB, Lambda etc… ) Terraform ( 両クラウドのリソースを可能な限りコードで統合管理) Backend Application CI / CD Ruby on Rails ( Cloud Run 上のコンテナとして稼働) GitHub Actions ( BE 含め、Dataflow のイメージビルドなどを自動化) Google Cloud Next Tokyo Proprietary
  5. 背景 主要システムの Google Cloud 移行完了 約 2 年前に、主力のデータベースを Amazon Aurora

    から Spanner へ、コンピューティング基盤を AWS Fargate から Cloud Run へと大規 模な移行を実施しました。 取り残された「アルバム機能」のデータ システムの大半が Google Cloud に集約される中、アルバム機能の データ ( 複数テーブル ) が Amazon DynamoDB 上で稼働を続けてお り、インフラの分断が起きていました。 Google Cloud Next Tokyo Proprietary
  6. なぜ DynamoDB だったのか? 進行する機能開発とのジレンマ 当時 Aurora から Spanner に移行しながら、アルバム機能の開発をし ていました。新機能のためのテーブルを

    Aurora に追加できず、開発の 手が止まってしまう課題に直面しました。 戦略的な DynamoDB の一時採用 開発スピードを維持するため、一時的なデータストアとして Amazon DynamoDB に複数テーブルを用意し管理する決断をしました。 Google Cloud Next Tokyo Proprietary
  7. 移行対象のテーブル テーブルの役割 トレンド 移行判断 広告関連 データ量 : 多 書き込み頻度 :

    低 データ量は約 2,700 万件とかなり多いですが、書き込みタイミングをサービス側で制御しや すかったため、先行して移行を完了しています。 アルバムへの アップロード状 況管理 データ量 : 多 書き込み頻度 : 高 「削除管理」と比較してデータ量・書き込み頻度ともに高く、初回から無停止移行に挑むには リスクが高いと判断しました。 アルバムから 削除管理 データ量 : 中 書き込み頻度 : 低 ( 今回の移行対象テーブル ) データ量は約 400 万件あるものの、書き込み頻度が比較的低 いため、無停止移行の最初のステップとして最適 ( 低リスク ) だと判断しました。 その他 - 他にも 6 つのアルバム関連テーブルが存在しており、今回のノウハウを活かして順次移行を 進めていく予定です。 Google Cloud Next Tokyo Proprietary
  8. テーブルを移行するためのステップ ツール選定 移行検証 自由に試せる Google Cloud の検証用 プロジェクトを作成し、要件を満たす最 適な移行ツールとアーキテクチャを選定 ・比較します。

    本番と同等の環境やデータを用いて、 移行処理にかかる時間や負荷、データ の完全性を事前にシミュレーションしま す。 Google Cloud Next Tokyo 環境構築 本番移行 本番でのオペレーション ミス ( 手作業 ) を徹底的に排除するため Terraform や GitHub Actions を用いて、再現性と安 全性の高い移行パイプラインを構築し ます。 十分な事前検証と改善を経たう えで本番移行を実施。この「 検 証 → 移行 」のサイクルを確立 し、残りのテーブルもひとつずつ 安全に移行していきます。 Proprietary
  9. 2 種類の Dataflow を用意 Batch Job 過去から蓄積された膨大なアルバムの既存データは、 Batch Job とし

    てまとめて Spanner へ流し込みます。これにより、大容量のデータを効 率的かつスピーディに移行できるようにしています。 Streaming Job バッチで移行している最中にも、ユーザーによって新しいアルバムが作 られたり更新されたりします。この差分データは、 Pub/Sub を経由した Streaming Job でリアルタイムに Spanner へ同期させます。 Google Cloud Next Tokyo Proprietary
  10. Dataflow のビルドとデプロイ GitHub ( Dataflow Code ) Google Cloud Source

    Code GitHub ( Terraform Code ) Artifact Registry Source Code Push ( Image ) GitHub Actions GitHub Actions Build Cloud Storage Upload ( Flex Template ) Apply Terraform ビルドの流れ デプロイの流れ Google Cloud Next Tokyo Dataflow Proprietary
  11. ひとつのテーブルを移行ステップ 1 DynamoDB Streams ON 2 DynamoDB Export 3 Dataflow

    Batch Job 4 Dataflow Streaming Job 5 移行データの全件検証 6 新データベース切り替え 移行中の更新データの記録を開始 作業開始時点のデータをエクスポート エクスポートしたデータを Spanner へ 移行中の更新データを Spanner へ BigQuery で整合性の検証 接続先データベースを Spanner へ Google Cloud Next Tokyo Proprietary
  12. 構成図 ( Step1 : DynamoDB Streams ON ) DynamoDB Streams

    を有 効化し、移行作業中に発 生する「新しいデータ」の 記録を開始します。 具体的には、変更イベント は Lambda を経由して Pub/Sub へ継続的にルー ティング・蓄積されます。 ( CDC : Change Data Capture ) Google Cloud AWS Lambda Pub/Sub Dataflow ( Streaming Job ) DynamoDB Stream DynamoDB Cloud Run Rails Application Spanner Export S3 Storage Transfer Service 参照先 ( 移行後 ) 参照先 ( 移行前 ) Cloud Storage Dataflow ( Batch Job ) データの流れ Google Cloud Next Tokyo Proprietary
  13. 構成図 ( Step2~3 : Dataflow Batch Job ) CDC 開始時点のスナップ

    ショットとして、 DynamoDB 内の「過去の 全データ」を AWS S3 へ一 括出力します。 S3 のデータを Storage Transfer Service で GCS へ高速転送し、 Dataflow の Batch Job を利用して Spanner へ一括で書き込 みます。 AWS Google Cloud Lambda Pub/Sub Dataflow ( Streaming Job ) DynamoDB Stream DynamoDB Cloud Run Rails Application Spanner Export S3 Storage Transfer Service 参照先 ( 移行後 ) 参照先 ( 移行前 ) Cloud Storage Dataflow ( Batch Job ) データの流れ Google Cloud Next Tokyo Proprietary
  14. 構成図 ( Step4 : Dataflow Streaming Job ) Pub/Sub に蓄積しておい

    た移行期間中の「差分 データ」を、 Dataflow のス トリーミング処理で Spanner に順次反映しま す。 AWS Google Cloud Lambda Dataflow ( Streaming Job ) DynamoDB Stream DynamoDB Step 3 の一括移行データ との重複は自動的にス キップ処理され、新旧デー タベースの完全な同期状 態を作り出します。 Pub/Sub Cloud Run Rails Application Spanner Export S3 Storage Transfer Service 参照先 ( 移行後 ) 参照先 ( 移行前 ) Cloud Storage Dataflow ( Batch Job ) データの流れ Google Cloud Next Tokyo Proprietary
  15. 構成図 ( Step5 : 移行データの全件検証 ) 新旧両データベース ( DynamoDB と

    Cloud Spanner ) からそれぞれ 全データを抽出し、検証基 盤である BigQuery へと 集約します。 AWS Google Cloud Export S3 集約したデータを全件比 較して両者の差分が完全 にゼロであることを確認 し、欠損のない確実な データ移行ができているこ とを担保します。 Spanner DynamoDB Storage Transfer Service Cloud Storage Dataflow ( Batch Job ) BigQuery Google Cloud Next Tokyo Proprietary
  16. 構成図 ( Step6 : 新データベース切り替え ) データ全件検証 ( Step 5

    ) で完全一致を確認した 後、 Cloud Run アプリケー ションの接続先を Spanner へ切り替えま す。 AWS Google Cloud Lambda Pub/Sub Dataflow ( Streaming Job ) DynamoDB Stream DynamoDB データの同期が追いつい ている状態で行うため、ダ ウンタイムなしでのシーム レスな移行が完了します。 Cloud Run Rails Application Spanner Export S3 Storage Transfer Service 参照先 ( 移行後 ) 参照先 ( 移行前 ) Cloud Storage Dataflow ( Batch Job ) データの流れ Google Cloud Next Tokyo Proprietary
  17. 構成図 AWS と Google Cloud に 跨る複雑な移行リソースを Terraform で一元管理 し、

    検証環境と全く同じ構成を 本番へ安全に展開できま す。 AWS Google Cloud Lambda Dataflow ( Streaming Job ) DynamoDB Stream DynamoDB コード管理することで他 テーブルへの「パイプライ ンの横展開や、移行完了 後のリソース削除もスムー ズに行えます。 Pub/Sub Cloud Run Rails Application Spanner Export S3 Storage Transfer Service 参照先 ( 移行後 ) 参照先 ( 移行前 ) Cloud Storage Dataflow ( Batch Job ) データの流れ Google Cloud Next Tokyo Proprietary
  18. まとめ : この構成による移行のメリット サービス停止を必要としない ( ゼロ ダウンタイム ) フルマネージド サービスによる容易なスケーリング管理

    本番環境への負荷・影響を最小化 データの重複排除による確実な整合性担保 Google Cloud Next Tokyo Proprietary
  19. まとめ : 安全・確実な移行のための 3 つのアプローチ 気軽に作れる 「使い捨て」検証環境 フラグ切り替えだけで 完結する Terraform

    運用 本番データを用いた 事前検証 個人単位・目的ごとに、いつでも自由にGC の検証用プロジェクトを作成できるよう既に整 備されています。 すぐに試せる環境があることで、移行の心理 的ハードルが下がり、徹底的な事前検証を実 現しています。 インフラ構築を可能な限りすべてIaC 化して います。 移行作業はコード上のフラグを順番に切り替 えていくだけにすることで、手作業によるオペ レーションミスを極力排除していました。 検証用のための Spanner インスタンス作成 し、本番データをコピーして移行負荷をシミュ レーションしました。 その結果をもとに本番移行時のSpanner の 一時的なスケールアウトなど安全対策を事前 に講じることができた。 また、事前に全行程の時間を計測し、タイム オーバーを防ぐ確実なスケジュールを策定し ます。 Google Cloud Next Tokyo Proprietary
  20. 型マッピング DynamoDB 型 Spanner 型 備考 N - Number INT64

    / FLOAT64 データによって INT64 と FLOAT64 を切り分ける S - String STRING B - Binary BYTES M - Map JSON S - String TIMESTAMP Google Cloud Next Tokyo Timestamp 型がないため String で microsec まで保存していた Proprietary
  21. スキーマ設計 DynamoDB Spanner Partition Key 日付 Primary Key (Composite PK)

    親テーブル ID Range Key UUID Primary Key (Composite PK) UUID Index 日付 親テーブル ID ・Spanner 移行を考慮し親テーブルの PK を事前に含めておく ・ホットスポット回避のため UUID をユニーク ID とする ・DynamoDB データ制約上 Partition Key は日付にする必要があった Google Cloud Next Tokyo Proprietary
  22. ひとつのテーブルを移行ステップ 1 DynamoDB Streams ON 2 DynamoDB Export 3 Dataflow

    Batch Job 4 Dataflow Streaming Job 5 移行データの全件検証 6 新データベース切り替え 移行中の更新データの記録を開始 作業開始時点のデータをエクスポート エクスポートしたデータを Spanner へ 移行中の更新データを Spanner へ BigQuery で整合性の検証 接続先データベースを Spanner へ Google Cloud Next Tokyo Proprietary
  23. ひとつのテーブルを移行ステップ 1 DynamoDB Streams ON 2 DynamoDB Export 3 Dataflow

    Batch Job 4 Dataflow Streaming Job 5 移行データの全件検証 6 新データベース切り替え 移行中の更新データの記録を開始 作業開始時点のデータをエクスポート エクスポートしたデータを Spanner へ 移行中の更新データを Spanner へ BigQuery で整合性の検証 接続先データベースを Spanner へ Google Cloud Next Tokyo Proprietary
  24. Dataflow Batch Job Dataflow 5 WriteDeadLetters Cloud Storage 1 JSON

    ファイルの内容を 取得 3 Mutation の作成 Cloud Storage 3 2 1 2 Java クラスにパース ReadItems ParseRecords CreateMutations 4 Spanner に書き込み 5 パースに失敗したものを Cloud Storage に出力 4 WriteRecords Spanner Google Cloud Next Tokyo Proprietary
  25. 原因 Spanner のレコード数が一致しない DynamoDB Spanner 2026-07-30 Primary Key created_at aaaaaaaa

    2026-07-30 aaaaaaaa 2026-07-31 aaaaaaaa 2026-07-31 aaaaaaaa 同一 UUID スキーマ構造の違いにより重複したデータが生成されていた データの特性上重複は許容できるものだった InsertOrUpdate Mutation を使用していたためエラーも発生しなかった Google Cloud Next Tokyo Proprietary
  26. 解消策 Spanner のレコード数が一致しない Insert のみに変更し 失敗したデータをログに出力する InsertOrUpdate を Insert に変更し、重複したデータを書き込むタイミングで

    エラーにする REPORT_FAILURES モードを使用して後続の処理は継続したまま 書き込みに失敗したデータをログに出力 Google Cloud Next Tokyo Proprietary
  27. Dataflow Batch Job Dataflow 1 JSON ファイルの内容を 7 取得 WriteDeadLetters

    2 Java クラスにパース 6 3 Mutation の作成 FlattenErrors Cloud Storage 3 2 1 4 Spanner に書き込み 5 Spanner への書き込みに Cloud Storage ReadItems ParseRecords CreateMutations 失敗したものを出力 6 パース時のエラーとマー ジ 4 5 WriteRecords LogFailedMutations 7 エラーログを Cloud Storage に書き込む Google Cloud Next Tokyo Spanner Proprietary
  28. ひとつのテーブルを移行ステップ 1 DynamoDB Streams ON 2 DynamoDB Export 3 Dataflow

    Batch Job 4 Dataflow Streaming Job 5 移行データの全件検証 6 新データベース切り替え 移行中の更新データの記録を開始 作業開始時点のデータをエクスポート エクスポートしたデータを Spanner へ 移行中の更新データを Spanner へ BigQuery で整合性の検証 接続先データベースを Spanner へ Google Cloud Next Tokyo Proprietary
  29. Dataflow Streaming Job Dataflow 1 Pub/Sub からメッセージを 6 7 FlattenErrors

    取得 WriteDeadLetters Cloud Storage 2 Java クラスにパース 3 Mutation の作成 4 ウィンドウ設定 5 Spanner への書き込み 6 パース時のエラーとマー 1 Pub/Sub ReadItems 2 ParseRecords 3 CreateMutations 4 ジ 7 エラーログを Cloud Storage に書き込む Google Cloud Next Tokyo CreateWindows 5 WriteRecords Spanner Proprietary
  30. Pub/Sub の推奨されていない機能 exactly-once 配信 指数バックオフ Dataflow には exactly-once 処理に関する独自 のメカニズムがある。パイプラインのパフォーマン

    スが低下する Dataflow では機能しない 即時再配信の再試行ポリシーを使用してサブスクリ プションを作成する必要がある メッセージの順序指定 デッドレター トピック Apache Beam では、要素の処理順序に関する厳 密なガイドラインが定義されていない。さらに、レイ テンシが増加し、パフォーマンスが低下する可能 性がある 正常な場合でもデッドレター トピックにメッセージが 配信されることがある。代わりに、パイプラインに デッドレター パターンを明示的に実装する必要が ある Google Cloud Docs: https://docs.cloud.google.com/dataflow/docs/concepts/streaming-with-cloud-pubsub?hl=ja#unsupported-features Google Cloud Next Tokyo Proprietary
  31. Pub/Sub の推奨されていない機能 exactly-once 配信 指数バックオフ Dataflow には exactly-once 処理に関する独自 のメカニズムがある。パイプラインのパフォーマン

    スが低下する Dataflow では機能しない すぐに再試行の再試行ポリシーを使用してサブスク リプションを作成する必要がある メッセージの順序指定 デッドレター トピック Apache Beam では、要素の処理順序に関する厳 密なガイドラインが定義されていない。さらに、レイ テンシが増加し、パフォーマンスが低下する可能 性がある 正常な場合でもデッドレター トピックにメッセージが 配信されることがある。代わりに、パイプラインに デッドレター パターンを明示的に実装する必要が ある Google Cloud Docs: https://docs.cloud.google.com/dataflow/docs/concepts/streaming-with-cloud-pubsub?hl=ja#unsupported-features Google Cloud Next Tokyo Proprietary
  32. Dataflow Streaming Job 1 Pub/Sub からメッセージを Dataflow 7 取得 8

    FlattenErrors 2 Java クラスにパース WriteDeadLetters Cloud Storage 3 更新すべきデータかチェッ ク 4 Mutation の作成 5 ウィンドウ設定 6 Spanner への書き込み 1 Pub/Sub ReadItems 2 ParseRecords 3 4 ConditionalRecords CreateMutations 5 7 パース時のエラーとマー ジ 8 エラーログを Google Cloud Storage に書き込む Google Cloud Next Tokyo CreateWindows 6 WriteRecords Spanner Proprietary
  33. 原因 Streaming Job が一部しか処理しない ワーカー VM のメモリ枯渇 Streaming Engine をオフにしたまま

    n1-standard-1 のマシンタイプを使用し ていた( Terraform でのデフォルト値をそのまま使用) ウィンドウ処理などのデータをワーカー VM のメモリに保持するようになってい た その結果メモリ枯渇しクラッシュが発生した Google Cloud Next Tokyo Proprietary
  34. 解決策 Streaming Job が一部しか処理しない Streaming Engine を有効にする パイプラインの実行をワーカー仮想マシン( VM)から Dataflow

    サービスの バックエンドに移動された ワーカー VM の CPU、メモリ、永続ディスク ストレージ リソースの消費量が削 減された 小さなワーカー マシンタイプでも最適に動作した Terraform Docs: https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dataflow_flex_template_job Google Cloud Docs: https://docs.cloud.google.com/dataflow/docs/streaming-engine Google Cloud Next Tokyo Proprietary
  35. Streaming Engineの役割 Streaming Engine なし Streaming Engine あり Worker VM

    Pub/Sub Streaming Engine データ取得 State 管理と 並び替え ウィンドウ集計 データ加工 Worker VM State 管理と 並び替え データ加工 ウィンドウ集計 外部へ書き込み Google Cloud Next Tokyo Spanner Pub/Sub データ取得 外部へ書き込み Spanner Proprietary
  36. ひとつのテーブルを移行ステップ 1 DynamoDB Streams ON 2 DynamoDB Export 3 Dataflow

    Batch Job 4 Dataflow Streaming Job 5 移行データの全件検証 6 新データベース切り替え 移行中の更新データの記録を開始 作業開始時点のデータをエクスポート エクスポートしたデータを Spanner へ 移行中の更新データを Spanner へ BigQuery で整合性の検証 接続先データベースを Spanner へ Google Cloud Next Tokyo Proprietary
  37. 新データベース切り替え DynamoDB / Spanner どちらにもアクセスでき るラッパークラスを実装 機能フラグで接続先を切り替えるため、呼び出し 側のコードの変更を最小限にする Standard Devices

    Ruby on Rails HTTPS Dynamoid DynamoDB Wrapper Class ※画像の置換方法 グレーボックスを選択し、 右ク リックで「画像を置換」 を選択 し、配置したい画像に差し替え てください。本テキストは削除し てください。 ActiveRecord Spanner Google Cloud Next Tokyo Proprietary
  38. 移行実績 Bulk + Streaming によるダウンタイム ゼロ移行を実現 Google Cloud Next Tokyo

    BigQuery 全件 検証でデータ 整合性を担保 移行開始から 終了まで約 3 時間 で完了 Proprietary
  39. 今後の展望 移行オペレーションの 迅速化(手順の集約) 他テーブル展開に向けた 移行モジュールの汎用化 より高トラフィックテーブ ルの移行 Pull Request 作成からレビューデプロ

    イに至る「 1 サイクル」の時間短縮は、 安全なガバナンスの維持とトレードオフ になるため困難 「移行ステップ数 ( PR の回数 ) 自体を 減らす」アプローチで作業時間の短縮を 図りたい。 Terraform のコードを特定のテーブル 構造に依存しない 「より汎用性と再利用性の高いモ ジュール」へと洗練させ 今後の移行コストを最小化していきた い。 今回の移行テーブルは比較的トラフィッ クの低いものが中心だったが、今後は読 み書きの多い高トラフィックなテーブルに も対象を広げていく Streaming の遅延や冪等性・整合性検 証の難易度が上がるため、これまで得た 知見を活かして安全に進めていきたい。 Google Cloud Next Tokyo Proprietary
  40. みつける 機能 Discovery Features ※ その他機能の詳細については HPをご覧ください © TimeTree, Inc.

    イベント発見 フリーワード検索 あなたの推しの活動予定、あなたの地域のイベント情報、あ キーワードを入力すると、関連するイベントが一覧で表示されま なたにあったイベントが見つかります。「気になる」ボタンを押 す。今日以降の予定が開始日順に並ぶので、近いものからチェッ しておけば、開催に合わせて通知を受け取れます。 クできます。 054
  41. Spanner 全文検索の 4 ステップ 1 2 検索対象を トークン化 検索インデックスを 作成

    SEARCH 関数でク エリ ORDER BY で並び 替え TOKENIZE_FULLTEXT() で カラムをトークン列に変換 CREATE SEARCH INDEX でインデックスを付与 SEARCH(col, 'keyword') でドキュメント検索 ORDER BY でソート Google Cloud Next Tokyo 3 4 Proprietary
  42. 制約① TIMESTAMP 型は ORDER BY に指定できない 原因 Spanner の SEARCH

    インデックスでは TIMESTAMP 型カラムを ORDER BY に使えない 解決策 UNIX_SECONDS() で INT64 に変換して格納する Google Cloud Next Tokyo Proprietary
  43. 制約② ORDER BY は既存インデックスに追加できない 原因 ORDER BY を後から既存の SEARCH インデックス

    に追加することはできない 解決策 ORDER BY を含む v2 インデックスを新設し、 アプリ側のクエリも v2 を使うよう切り替える Google Cloud Next Tokyo Proprietary
  44. 制約③ ORDER BY 指定カラムは STORING に 書いてはいけない 原因 ORDER BY

    に指定したカラムは自動的にインデックスに含まれるため、 STORING に書くと DDL 適用エラーになるという暗黙的なルールが存在す る 公式ドキュメントに記載がなくエラーで発覚 エラー内容 Index search_tokens_fulltext_index_v2 specifies stored column created_at_unix already specified as primary key. Google Cloud Next Tokyo Proprietary