ものづくりのブログ

うちのネコを題材にしたものづくりができたらいいなと思っていろいろ奮闘してます。

【Firestore】カウントアップする ID を返す API 作成

Firestore でカウントアップする ID を返す API 作成したのでその時のメモをここに残します。

Firestore

Firestore は、Google 社が提供する NoSQL 型のデータベースです。

用途と主な利用ケース

サーバーレスの NoSQL ドキュメントデータベースで、構造化されたデータの格納やリアルタイム同期を可能にします。例えば、チャットアプリやコラボレーションツール、複雑なクエリが必要なアプリに適しています。データのオフラインアクセスや同期が必要なモバイルアプリにも有効です​。

データモデル

ドキュメントベースのデータモデルを採用し、コレクションとドキュメントでデータを階層的に構造化できます。複雑なクエリやデータフィルタリングが可能で、リアルタイム更新をサポートしています​。

パフォーマンスとスケーラビリティ

自動スケーリングが可能で、大規模なアプリケーションでも一貫したパフォーマンスを提供します。大規模なデータを扱う場合でも安定した性能を保てるのが特長です​。

データの永続性と信頼性

データは自動的に複数の場所にレプリケートされ、高い信頼性と永続性を保証します。ACIDトランザクションをサポートし、データ整合性も確保しています​。

料金

使った分だけ支払う「従量課金制」を採用しています。料金は、主に次の3つの要素から構成されています。

操作(リード、ライト、削除)

  • リード: 100,000回のリード操作ごとに $0.06
  • ライト: 100,000回のライト操作ごとに $0.18
  • 削除: 100,000回の削除操作ごとに $0.02
無料枠

Firestore には無料枠があり、以下を含みます:

  • 1日あたり 50,000回のドキュメントリード
  • 1日あたり 20,000回のドキュメントライトと削除
  • 1GBのデータ保存
  • 10GB/月のネットワーク送信

サンプル

パッケージ

functions-framework==3.*
google-cloud-firestore

コード

import functions_framework
import json
import logging
import sys
import threading
import traceback

from datetime import datetime
from google.cloud import firestore

logger = logging.getLogger()
for h in logger.handlers:
    logger.removeHandler(h)
h = logging.StreamHandler(sys.stdout)
FORMAT = "%(levelname)s [%(funcName)s] %(message)s"
h.setFormatter(logging.Formatter(FORMAT))
logger.addHandler(h)
logger.setLevel(logging.DEBUG)

COUNTER_COLLECTION = "counters"
COUNTER_DOCUMENT = "sample_id"
PREFIX = "sample-id"
db = firestore.Client.from_service_account_json("./sample_client.json")


def _generate_sample_id():

    counter = 0
    lock = threading.Lock()

    counter_doc_ref = db.collection(COUNTER_COLLECTION).document(COUNTER_DOCUMENT)
    snapshot = counter_doc_ref.get()

    if snapshot.exists:
        counter = snapshot.get("count")
    else:
        counter_doc_ref.set({"count": counter})

    with lock:
        counter += 1
        sample_id = counter
        counter_doc_ref.set({"count": sample_id})

    current_date = datetime.now().strftime("%Y%m%d")
    sample_id = f"{PREFIX}-{current_date}-{str(sample_id).zfill(8)}"

    return sample_id


@functions_framework.http
def ss_gec_supporter_dev(request):
    try:
        _dict = {
            "request_method": request.method,
            "request_headers": request.headers,
            "request_args": request.args,
            "request_remote_addr": request.remote_addr,
            "request.user_agent": request.user_agent,
        }
        logger.debug(_dict)

        sample_id, _message, _status = None, None, 200
        sample_id = _generate_sample_id()

    except Exception as e:
        logger.error(f"{traceback.format_exc()}")
        _status = 500
        _message = e

    finally:
        results = {
            "sample_id": sample_id,
            "message": _message,
        }
        logger.info(results)
        return (json.dumps(results, indent=2), _status)

デプロイ

$ gcloud functions deploy sample_api \
  --gen2 \
  --runtime python312 \
  --region asia-northeast1 \
  --trigger-http \
  --allow-unauthenticated