フラミナル

考え方や調べたことを書き殴ります。IT技術系記事多め

Open Saves をざっと眺める

Open Saves の概要: ゲーム向けのオープンソースのクラウドネイティブ ストレージ | Google Cloud Blog が紹介されていたのでみてみます。

開発チームが Cloud StorageMemorystoreFirestore  のいずれのストレージ ソリューションを使用するかについて技術上の意思決定を行わなくても、ゲームデータを保存できるようになります。

組み込みキャッシュ システムにより、Open Saves はアクセス頻度とデータサイズに基づいてデータ配置を最適化し、小型バイナリ オブジェクトの低レイテンシと大型オブジェクトの高スループットをともに実現します。

ハンズオン

ここにハンズオンがあるのでやってみる。 open-saves/deploying.md at main · googleforgames/open-saves

実行結果

go run examples/grpc-client/main.go -address=$ENDPOINT:443
go: downloading google.golang.org/grpc v1.48.0
go: downloading google.golang.org/api v0.90.0
go: downloading google.golang.org/protobuf v1.28.1
go: downloading google.golang.org/genproto v0.0.0-20220728213248-dd149ef739b9
go: downloading golang.org/x/net v0.0.0-20220728211354-c7608f3a8462
go: downloading golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c
go: downloading cloud.google.com/go v0.103.0

// datastore 上に `store` を作成
2022/10/16 23:45:10 successfully created store: key:"22149207-f452-4874-ad6b-1e65dec8fb67" name:"user-store" owner_id:"admin1" created_at:{seconds:1665931508 nanos:970569000} updated_at:{seconds:1665931508 nanos:970569000}

// user-1234の record を作成
2022/10/16 23:45:11 created record: key:"user-1234" properties:{key:"username" value:{type:STRING string_value:"tryndamere"}} owner_id:"admin1" created_at:{seconds:1665931510 nanos:103403000} updated_at:{seconds:1665931510 nanos:103403000}

// user-1234の record を取得
2022/10/16 23:45:11 got record: key:"user-1234" properties:{key:"username" value:{type:STRING string_value:"tryndamere"}} owner_id:"admin1" created_at:{seconds:1665931510 nanos:103403000} updated_at:{seconds:1665931510 nanos:103403000}

// game-1-replay-1234 で blob 用の record を datastore に追加し、GCS にもアップロード
2022/10/16 23:45:12 created record: key:"game-1-replay-1234" owner_id:"admin1" created_at:{seconds:1665931511 nanos:505224000} updated_at:{seconds:1665931511 nanos:505224000}
2022/10/16 23:45:12 finished sending blob
2022/10/16 23:45:16 sent 640000 bytes for blob on stream

OpenSaves がやってること

  • CreateRecord / CreateBlob を使うと任意のところに書き込んでくれる
    • CreateRecordの場合、デフォで datastore と redis に書き込む
    • キャッシュしない設定もあった
  • Blob を作成する場合は、CreateBlobで gRPC stream を生成して、 CreateRecord して record key を作っておき、それを使ってリクエストを投げる
func createBlob(ctx context.Context, c pb.OpenSavesClient, storeKey, recordKey string, content []byte) error {
    cbc, err := c.CreateBlob(ctx)
    if err != nil {
        return err
    }

    err = cbc.Send(&pb.CreateBlobRequest{
        Request: &pb.CreateBlobRequest_Metadata{
            Metadata: &pb.BlobMetadata{
                StoreKey:  storeKey,
                RecordKey: recordKey,
                Size:      int64(len(content)),
            },
        },
    })
    if err != nil {
        return fmt.Errorf("CreateBlobClient.Send failed on sending metadata: %w", err)
    }

データの削除について

  • チュートリアルを読むと 「Blobの削除は削除フラグの付与が行われ裏で消す」 らしい
  • ただその消し方が open-saves-gc というコンテナを動かす手順になってる
  • 実際にコードを読んだ
    • 削除をする際は Open Saves Server に削除リクエストを送る
    • この時削除対象の Blob を指し示す Record を StatusPendingDeletion 状態にする(ステートマシン)
    • 一方で別に collector を動作させ、expiration を設定し起動すると削除期限があり保持期限が切れているデータを削除してくれる
// Status represents a current blob status.
//
// # Life of a Blob
//
// [New Record created] --> [new Status entity with StatusInitializing]
//
//                        /               \
//                       / fail            \  success
//                      v                   v
//               [StatusError]            [StatusReady]
//                      |       x            |
//     Upload new blob  |        \ fail      | Record deleted or new blob uploaded
//           or         |         \          v
//    delete the record |          -------[StatusPendingDeletion]
//                      v                  /
// [Delete the blob entity] <-------------/   Garbage collection

別のクラウドも使えるらしい

  • コード読んでたら cloud という設定があった。cache / blob / datastore をクラウド問わず自由に設定できるようにするのがコンセプトの模様
  • ただいま時点は gcp しかないし、gcs / metadab(datastore / redis) 決め打ちぽい

たまたま見かけた grpc におけるヘルスチェックの仕組み。

感想

  • まだこれからという印象
  • 1 gRPC でストレージ、キャッシュ、データストアを意識しなくていいのは楽
  • ただユースケースが思いついてない
  • GCS / DB / Redis のライブラリ使って書き込みを個別にやるのと比較して、一箇所挟まれる+ロジックを理解する必要があるのがネック