🔖

はじめての設計とトランザクション処理で学んだ4つのこと【Python】

に公開

初めに

日々の業務をこなしていると、成果を追い求めるあまりついつい振り返りをおろそかにしちゃいますよね。

AIが発達したこの時代に振り返りをしないと自分の中にスキルがたまっていかない感覚があり、長期的に何も残らないのではという危機感を持っています。

なので定期的に業務を振り返ってみようと思いたちました。
特に初めて触ったことを重点的に振り返り、自分のスキルにしたいなと考えています。

学び

  • 自動化するときは「人間の入る余地を極限まで削る」ことを考えるといい、というしごく当たり前な結論に至った。
  • 設計は『「目的」と「ゴール」を考えそれを達成するために何するか』という、ビジネスでは耳タコなほど言われることが重要になることを再確認した。
  • 無駄に手間を増やすのではなく、変数と定数を意識して「1度だけ確認すればいい所」と「毎月確認が必要な所」に分けると考えやすそう。
  • "Garbage In, Garbage Out"という素晴らしい開発原則を知れた

やったこと

トランザクション処理

Javaエンジニアしているときに飽きるほど書いた処理。

Javaだとなんとなく設計書見て必要だから書く、くらいの認識だったのですがふとPythonでどう書くのだろうと疑問に思いました。
というかPythonとDBってどう接続するんだろう、ってこともわからない状態です。

たまたまSnowflake上の既存テーブルの一部を他データに差し替えるという業務が生まれたので、何とかしてトランザクション処理できないかなと頭をひねったのがこれです。

Snowflakeを使用していたので、まずそちらとの接続が必要でした。

conn_snowflake.py
import snowflake.connector

conn = snowflake.connector.connect(
    user='YOUR_USER',
    password='YOUR_PASSWORD',
    account='YOUR_ACCOUNT', 
    warehouse='COMPUTE_WH',
    database='MY_DB',
    schema='PUBLIC',
    role='SYSADMIN',
)

# 2. カーソル作成
cur = conn.cursor()

こんな感じのやつ。
connがデータベースとの通信経路そのものであり、今回はSnowflakeとPyhtonをつなぐパイプラインとなっている。
curはSQL文をDBに送信して、結果を受け取るための操作窓口。connを通じで処理を行うための手段って感じでしょうか。

cur.execute(sql)と処理を流すことでPythonからSQL処理を実行できるそう。
下記のようなコードを書いてトランザクション処理を実行しました。

transaction.py
delete_sql = f"""
DELETE FROM DB_name.Schema_name.Table_name
WHERE xxx IN ('xxx', 'yyy', 'zzz');
"""

insert_sql = f"""
INSERT INTO DB_name.Schema_name.Table_name (
    xxx,  -- 入れ替えるカラムを記載
    xxx,
    xxx,
    xxx,
    xxx,
    xxx
)
SELECT
    xxx,  -- 同じカラムを取ってくる
    xxx,
    xxx,
    xxx,
    xxx,
    xxx
FROM
    DB_name.Schema_name.Table_name2
WHERE
    xxx IN ('xxx', 'yyy', 'zzz');
"""

try:
    print("トランザクションを開始します")
    cur.execute("BEGIN;") # トランザクション開始の合図

    print("3メディアの既存データを削除中...")
    cur.execute(delete_sql)
    print("削除完了です")

    print("テーブル2からデータを挿入中...")
    cur.execute(insert_sql)

    cur.execute("COMMIT;") # ここまで来て初めて処理が確定する
    print("成功: トランザクションをコミットしました。")

except Exception as e:
    cur.execute("ROLLBACK;") # 何かしらエラーがあればロールバックする
    print(f"エラー: トランザクションをロールバックしました。{e}")

上記によってカラムを削除したけど更新できなかった、という状態を避けることができます。

各DBによってトランザクションの文法は違えど、BEGIN;などの処理をSQLの前後に入れることによって実現できそうですね。

品質保証の仕組み設計

設計書作成。
いかにも上流って感じの言葉ですね。

Javaエンジニアやってたころはまるっと作成したこと自体なく、製造段階で修正が発生した時にちょくちょく直してたくらい。

なので書き方もわからないし、今入っている案件のフォーマットとかも知らなかったです。

その状態で「エラー起こんないような仕組み考えて実装してください」と仕事を丸投げしていただきました。
抽象的な投げ方をしていただくこと自体、ある程度信頼されている感じがしてうれしいものの、上記の状態だったので結構焦りました。

とはいえ実装まで任せてもらえたので、この業務を進めるうえで必要なことは「実装前に共通認識をそろえること」だけやればいいと考え、Javaの要件定義書みたいながっちりした設計書はいらないなと考え文書にまとめることにしました。

書いた内容は大まかに下記のようになったと思います。

  • 格納データにトラブルが発生しない仕組みと、発生したことを検知する仕組みを作る
  • 「①ロジックの正当性を1度だけ確認する場所」と「②毎月継続して見る場所」を分けて考え、②だけ定期実行のフローに組み込む
  • ①は想定されるケースを網羅してテストケースを作成し、想定通りの挙動になるかを確認。その後は同じ処理が流れると考え問題がない限り触らない。
  • ②はGIGO原則にのっとり、正しいデータが処理に流れているかを確認。判定を自動化してそれらがすべてTrueなら問題なしという設計にする。

自分でもよくできたなと思う点が、毎月見る処理を複雑にしすぎないところかなと思います。

「エラーがないように」と言われるとどうしてもダブルチェックや複数個所の確認などが最初に考え付いてしまいますが、毎回それやるのは工数的にしんどいですし何より慣れてきたら結局適当になってヒューマンエラーが起こりやすそうです。

上記を考えてちゃんと見るべき処理は1回だけ見て、それ以外はその処理が正しいと考えデータの質にだけ目を向けること、それも人の手を介したものでなくコードで実行した結果がどうかを見るだけ、という簡素化ができたなと。

HITLみたいな考えの逆で、意思決定が不要な部分では徹底して人間の介入を減らすというのは今後も意識したらよさそうだなと思っています。

感想

以前「上司やら先輩エンジニアがいないから設計とかキャッチアップの仕方がわからない」という悩みを抱えていたのですが、いざやってみろと言われるとなんとなくできることがわかってきました。

何か正解がある、という風に強く思いすぎていた気がします。

結局何やりたいんだっけ、を考えて、それに必要なことをやっていくだけで問題がなく、他組織に移ったとしてもそこのルールやフォーマットを踏襲しながら同じことやるだけだなと感じています。

最近はいろいろな仕事に触れるため手を上げたり発表したりと、自主的にいろいろやるようになっています。
よくいうやらされ仕事より作る仕事のほうが楽しいというのを実感しています。

記事にすると頭に残りやすいので、今後も定期的に書いていこうかなと思っています。

Discussion