OmniAICreator/Qiita-1.07M huggingface dataset:
インターン体験記(株式会社エイチーム)

# はじめに
2024年11月1日～11月31日の一ヶ月間で行われた株式会社エイチームさんの就業型インターン体験記をまとめさせていただきます。
当インターンには1ヶ月で週1～3日の計9日参加し、BPO推進室の元でフロントエンドの実装を行っていきました。
＊自分の方が学校の授業の合間を縫って今回のインターンに参加させていただいたため、かなり限られた時間での参加となりました｡｡｡

# インターン概要
内容: 株式会社エイチームライフデザインに所属し、ライフエンディングの総合サイトであるLife.に関わるタスクを行う
場所: 愛知県 名古屋市 大名古屋ビルヂング31、32階 (名古屋駅の目の前)
期間: おおよそ一ヶ月 (長い人で2ヶ月、筆者は1ヶ月)
時間: 10:00 ~ 19:00
使用言語・FW: TypeScript + React

# 1日目
・初期セットアップ・環境構築
・オフィスツアー
・チームに自己紹介
・目標・ロードマップ作成
初日はインターンの初期準備の段階で元々使用予定だったPC(MacbookPro)の方がうまく起動せずに使いない状況となり、急遽PCを変更してもらうというハプニングがあり、PCの初期準備だけで業務が終わってしまいました｡｡｡

# 2日目
・VScode等の環境構築Github設定

2日目では環境構築はスムーズにいったのですが、プロジェクトがうまく立ち上がらずエラーを吐いてしまい、結局直せずに終わってしまいました。

# 3日目
・メンターとペアプロでプロジェクトエラー対処
・実装作業

3日目では、メンターが技術メンターに自分の今の状況を伝えてくれて、午前中に技術メンターとのペアプロでエラー対処を行いました。
無事にエラーが治りプロジェクト立ち上がったので、各タスクの作業に移りました。
低難度：6個、中難度：5個、高難度：8個
この日は低難度を5/6終わらせることができた。
低難度のタスクとしては、簡単な文言の訂正やアイコン表示等ReactやJavascriptの知識がある程度理解できていればできるタスクばかりでした。

# 4日目
実装作業

4日目では、3日目の作業の続きを行いました。
この日は低難度を全部終わらせて、中難度のタスクをやっていきました。
中難度では、主サーバーサイド側のタスクが中心でした。
また、低難度で終わっているタスクのレビューを見て対応していくことも同時進行で進めていきました。
muiのライブラリを入れる際、間違えて違うライブラリを入れてしまい、エラーが出てしまうというミスをしてしまい、時間を溶かしてしまった。


# 5日目
実装作業、ランチ(営業)

5日目では、4日目の続きを主にやっていき、中難度のタスクを全て終わらせ、たまっていたレビューの解消をやっていきました。
そして、お昼休みで、部署の営業職の社員さんとランチに行き、営業職の仕事であったり、今の自分の就活の悩み等を聞いてもらったりかなり有意義な時間を過ごすことができました。


# 6日目
実装作業

6日目では、主にレビューの解消時間として作業をしていきました。
その中で、PRを出すときに、読み手に伝わるようにこういうこと意識してみたら？とメンターにアドバイスをもらい、そのアドバイス通りにこれまで出したPRを改変する作業を行っていきました。
これは普段の開発でも活かせることだと思い自分の中に落とし込めるように意識しながらやっていきました。

# 7日目
実装作業

7日目は高難易度のタスクをこなしていきました。
高難易度のタスクは、パッケージアップデートやバージョンアップ、保守提案など普段の開発ではあまり行われない作業が多くてどこから手をつけた方がいいのかスケジューリングが難しかったです。

# 8日目
実装作業

8日目も高難易度のタスクそしてレビューの解消時間に充てました。
レビューの方では、最初の方だといろいろ指摘事項がありましたが、段々とPRの出し方にも慣れて一回でレビューも通るようになっていったのがうれしかったです。
高難易度のタスクでは、簡易的なスケジュールを作りそれを元に進めていきました。
タスクを一つ終わらせることができました。

# 最終日
実装作業、会社説明会、ランチ(デザイナー職)、クロージング作業

最終日は高難易度のタスクを中心に作業を進めていきました。
パッケージのバージョンアップに苦戦してなかなか思うように作業ができませんでした。
お昼休みではデザイナー職の社員さんとランチに行き、自分はデザイン系統が苦手なので、いろいろデザインについてお話ができ、今後の作品制作でも役に立つことばかりでとても充実しました。
クロージングでは、最初に立てた目標にどれだけ近づいているかの確認や、インターンを通しての感想等をメンターとの1on1で話したりしました。
会社説明会では、人事の方と対面でお話しすることができ、メンターの方からの評価を頂いたり、改めてのエイチームさんの説明等をしていただきました。
最後には所属させていただいた部署の皆さんにお礼の挨拶をして無事に1ヶ月間のインターンを終えることができました。

全体を通して
実装課題に関して
手のつけやすいレベルの課題から入って肩を慣らしつつ、パッケージのアプデのようななかなか個人では行わないタスクで仕事としてのエンジニアリングやフレームワーク自体への理解を深めることができたと思います。

人との交流に関して
社員の方とは主にランチの時間や組んでいただく1on1を通してたくさんお話しさせていただきました。

会社に関して
名古屋駅からも徒歩で５分ほどでアクセスも良く、オフィスも広く整備された環境で仕事ができました。会社の雰囲気も活気があり、働いている方々もとにかく優しい方々ばかりなのでいつでも相談できる環境で助かりました。
そしてエンジニア同士の繋がりだけでなく会社全体の繋がりを意識されていることに感銘を受けました。

最後に
1ヶ月のインターンを通して、エンジニアとしての働き方や、エンジニア以外の社員さんとのコミュニケーションを取ることの大切さを学ぶことができました。筆者のエンジニア・インターンとしては学校インターン含めて2回目でしたが、エイチームさんでインターンをやれたことで、就活の軸がより鮮明になり、また今後の開発にも非常に活かせることばかりでほんとに貴重な体験をさせていただいたと実感しております。
Raspberry Pi PicoでMMA8452Qを使う

## やってること
加速度センサmma8452qで加速度を測定し、表示させる。

## 環境
マイコン：Raspberry Pi Pico
プログラミング言語：MicroPython (Thony)
加速度センサ：[MMA8452Q](https://akizukidenshi.com/catalog/g/g112069/)

## ライブラリのインストール
Picoへ事前に以下ライブラリをインストールしておいてください。
[GitHub](https://github.com/jposada202020/MicroPython_MMA8452Q/tree/master)→micropython_mma8452q→「i2c_helpers.py」と「mma8452q.py」をダウンロード→Picoをパソコンに接続してThonyを開く→左下のPicoの内部ストレージのlibフォルダを開く→何もない所を右クリックで新しいディレクトリを作成→名前を「micropython_mma8452q」にしておく→その中にダウンロードした2つのファイルを入れる→libフォルダにも「mma8452q.py」を入れておく（こうするとうまくいく。）

## 接続
| Pico | BME 280 |
|:-:|:-:|
|GP0|SDA|
|GP1|SCL|
|VSYS|3.3v|
|GND|GND|

### 確認
以下のプログラムを実行して、BME280が正しく接続されているか確認
```py
from machine import I2C, Pin
i2c = I2C(0, scl=Pin(1), sda=Pin(0), freq=100000)
print(i2c.scan())
```

何か数字が表示されればOK。されなかったら接続を再確認。

## プログラム

```py:mma8452q.py
import time
import utime
import machine
from machine import Pin, I2C
import mma8452q
  
# MMA8452 (I2C setting)
i2c = I2C(0, scl=Pin(1), sda=Pin(0), freq=100000)
mma = mma8452q.MMA8452Q(i2c, address=0x1D)  # アドレスが0x1Dの場合

        
# Listen for connections, serve client
while True:
    try:
        x, y, z = mma.acceleration
        #print(f"X: {x:0.2f} m/s^2 | Y: {y:0.2f} m/s^2 | Z: {z:0.2f} m/s^2")
        #print(f"X: {x:0.2f} m/s^2 | Y: {y:0.2f} m/s^2 | Z: {z:0.2f} m/s^2       ", end="\r")
        print(f"{x:0.2f} m/s^2 | {y:0.2f} m/s^2 | {z:0.2f} m/s^2")
        time.sleep(0.1)
    except OSError as e:
        print('Process error:', e)
```

## 終わり
csvファイルにデータを記録して、Pico本体に保存するプログラムもあります。必要でしたらコメントで教えてください。何かご質問等があればとりあえず聞いてください。
ABC380Eを解いた【UnionFind】


筆者はレート800前後の茶～緑コーダ

ABC380のE問題を解いていく
    
https://atcoder.jp/contests/abc380/tasks/abc380_e

# 実装コード

UnionFindを使う
集団に左右端の位置を覚えさせておき
塗り替えた場所の隣と色が同じになった場合は
それらの集合と合体させればOK

```python:main.py
import sys
def rI(): return int(sys.stdin.readline().rstrip())
def rLI(): return list(map(int,sys.stdin.readline().rstrip().split()))
def rI1(): return (int(sys.stdin.readline().rstrip())-1)
def rLI1(): return list(map(lambda a:int(a)-1,sys.stdin.readline().rstrip().split()))
def rS(): return sys.stdin.readline().rstrip()
def rLS(): return list(sys.stdin.readline().rstrip().split())
def err(*args): print(*args, file=sys.stderr)


class UnionFind:
    def __init__(self, n):
        self.parent = [-1] * -~n
        self.rank = [1] * -~n
        self.label = list(range(-~n))
        self.left = list(range(-~n))
        self.right = list(range(-~n))
        self.count = n
        self.c_cnt = [1 for i in range(-~n)]

    def root(self, x):
        while (p := self.parent[x]) != -1:
            x = p
        return x

    def union(self, x, y):
        xr = self.root(x)
        yr = self.root(y)
        nl = min(self.left[xr],self.left[yr])
        nr = max(self.right[xr],self.right[yr])
        if xr == yr:
            return
        elif (rx := self.rank[xr]) < (ry := self.rank[yr]):
            self.parent[xr] = yr
            self.rank[yr] += rx
            self.left[yr] = nl
            self.right[yr] = nr
            # err(self.label[yr],nl,nr)
        else:
            self.parent[yr] = xr
            self.rank[xr] += ry
            self.left[xr] = nl
            self.right[xr] = nr
            # err(self.label[xr],nl,nr)
        self.count -= 1

    def same(self, x, y):
        return self.root(x) == self.root(y)
    
    def get_left(self, x):
        return self.left[self.root(x)]
    def get_right(self, x):
        return self.right[self.root(x)]
    def get_label(self, x):
        return self.label[self.root(x)]
    def set_label(self, x, c):
        xr = self.root(x)
        rx = self.rank[xr]
        xc = self.get_label(x)
        self.label[xr] = c
        self.c_cnt[xc] -= rx
        self.c_cnt[c] += rx
    


def main():
    N, Q = rLI()
    
    UF= UnionFind(N)
    
    for _ in range(Q):
        q = rLI()
        if q[0] == 1:
            x,c=q[1:]
            UF.set_label(x,c)
            lx = UF.get_left(x)
            rx = UF.get_right(x)
            if lx>1 and UF.get_label(lx-1) == c:
                UF.union(x,lx-1)
            if rx<N and UF.get_label(rx+1) == c:
                UF.union(x,rx+1)
            # err([UF.get_label(i+1) for i in range(N)])
        if q[0] == 2:
            c=q[1]
            print(UF.c_cnt[c])
            
    
if __name__ == '__main__':
    main()

```

# まとめ

ABC380Eを解いた。実装にはUnionFindを使用した。

確か時間内に解けなかったのが残念だった記憶だが、
実装自体にはなんら苦労はなかったので
もっと早く実装する能力が求められるようだ。
Next.jsのnext buildが何をしているか

# buildとは

[wiki](https://ja.wikipedia.org/wiki/%E3%83%93%E3%83%AB%E3%83%89_(%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2))から引用

>ソフトウェアのビルド（英: build）は、プログラミング言語で書かれたソースコードファイルや各種リソースファイルを独立したソフトウェア生成物に変換するコンピュータ上で実行されるプロセス、またはその結果を指す。

自分なりの解釈だと、人間の書いたプログラムを機会が読めるようにする流れを全て行うことがビルド。その中にはコンパイルだとかリンクだとかパッケージ化が含まれている。という解釈。

# じゃあ`next build`は？

[ドキュメント](https://nextjs-ja-translation-docs.vercel.app/docs/api-reference/cli#build)から引用

> `next build` は、アプリケーションに最適化された本番のビルドを作成します。出力にはそれぞれのルーティングに関する情報が表示されます。
**Size** – クライアントサイドのページへ移動する際にダウンロードされるアセットの数。各ルーティングのサイズには、依存関係のみが含まれます。
**First Load JS** – ページへアクセスしたときにサーバーからダウンロードされたアセットの数。すべてのアプリケーションで共有される JS の量は、別の指標として表示されます。

細かく調べてみたら色々しているみたい。

### 1.TypeScriptの型チェック

- ビルド開始時にTSの型チェックを行う
- 問題があるとエラーを返して終了する

### 2.静的ファイルのコピー

<details><summary>CDNとは</summary>

[wiki](https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%B3%E3%83%86%E3%83%B3%E3%83%84%E3%83%87%E3%83%AA%E3%83%90%E3%83%AA%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF)より

> CDNとは、ウェブコンテンツをインターネット経由で配信するために最適化されたネットワークのことである。

デプロイすると、オリジンサーバーにhtml,css,jsが保存される。その後CDNと言われる各地にある中継地点にコピーが保存され、ユーザーはそこからデータを取得する。そうすることで高速化とオリジンサーバーの負荷分散がされる

</details>

- アプリケーションのpublicディレクトリ内の静的ファイルをチェック
- コピーして.nextディレクトリや本番用の静的ホスティング環境に配置する
- コピーファイルは近くのCDNに配置され、最も近いエッジサーバーから高速に配信されるようになる

### 3.JavaScriptのトランスパイルとバンドル

- トランスパイル
    - Next.jsは、Babelを使用してES6+やTypeScriptをブラウザが解釈できるJavaScriptに変換する
- バンドル
    - Webpackを使用して、複数のJavaScriptファイルをまとめ、重複したコードを削除する
    - 各ページの依存関係を最適化し、それぞれに必要な最小限のコードを含むバンドルを生成

### 4.ページの処理

Next.jsはルーティングと各ページごとの要件に応じて処理を変更する

1\. 静的ページ作成

- ビルド時にHTMLとCSSに画像や動画の参照が埋め込まれ、生成されたページがそのまま配信できる形になる

2\. サーバーレンダリング用の準備

- ビルド時にCSSやJavaScriptを最適化し、各ページに必要な最小限のファイルに分割される

### 5.アセットの最適化

<details><summary>アセットを簡単に知る</summary>
アセットとは

[IT用語辞典](https://e-words.jp/w/%E3%82%A2%E3%82%BB%E3%83%83%E3%83%88.html#google_vignette)から引用
> ビデオゲームやデジタルコンテンツの制作・開発では、その構成要素となる画像や3Dモデル、音楽、効果音などの素材のことをアセットということがある。

</details>

1\. cssの最適化

- Tailwind CSSやPostCSSなどを使用している際、未使用のcssを削除

2\. 画像の最適化

- ローカル画像や`/public`に保存された画像がビルド時に画像最適化され、CDNに置かれる

### 6.クライアントとサーバーの分離

Next.jsではビルド時にクライアント用とサーバー用のコードが明確に分離される

- クライアント側
    - ブラウザで実行されるコードにトランスパイル（変換）され、最適化された状態で準備
- サーバー側
    - サーバー用のエンドポイント（サーバーレス関数など）としてビルドされる

# 簡単なまとめ

Next.jsのビルドは一般的なコンパイル、リンク、パッケージ化に加えて静的サイト生成、サーバーレス化、クライアント/サーバー分離、最適化などアプリを効率的に提供する仕組みが備わっているぽい
LambdaとStep Functionsでのデータ登録・変換プロセス自動化

### 実験データの登録から変換までを自動化したい

最近、あるシステムでデータをS3にアップロードすると、そこからデータを登録し、さらにセンサー情報を解析したうえでデータ変換を行う仕組みを作る必要がありました。この一連の流れをAWSのサービスを活用して自動化しようというのが今回の目的です。

具体的には、
- **データ登録処理**: S3にアップロードされた`sample.json`を読み込み、データをデータベースに登録する。
- **センサー解析処理**: 登録されたデータをもとにセンサー情報を解析し、ネットワーク名を推定してデータベースに保存する。
- **データ変換処理**: 上記の結果を用いて、最終的にデータ変換するステートマシンを起動する。

この流れをStep Functionsで構築することで、処理の順序を管理しつつ、問題が発生した際にもわかりやすく追跡できる設計を目指しました。

前提として、データ登録処理とセンサー解析処理それぞれ独立したLambda関数として実装されている状態です。

---

### 見えてきた課題

この計画を進める中で、いくつかの課題が明らかになりました。

#### 1. 必要なデータが分散している
- **データ登録処理**と**センサー解析処理**がそれぞれ独立したLambda関数として実装されており、必要な情報が片方でしか手に入らない状況でした。
- 例えば、`sample.json`の情報はデータ登録処理で取得される一方で、センサーのネットワーク名はセンサー解析処理でしか取得できません。この2つを統合しないと、次のデータ変換処理に必要なパラメータを生成できません。

#### 2. トランザクションの一貫性がない
- 1つ目のLambda関数が成功し、データベースに登録が完了しても、2つ目のLambda関数で失敗すると、中途半端な状態がデータベースに残ってしまいます。
- これでは後続の処理が正しく動作しない可能性があります。

#### 3. 実行時間の制限
- Lambda関数の最大実行時間は15分。1つのLambdaにすべての処理を詰め込むと、この制限を超える可能性がありました。

---

### 解決策の検討

課題を解決するために、いくつかのアプローチが考えられました。

#### **案1: Lambda関数を統合する**
- データ登録とセンサー解析を1つのLambda関数で処理する。
- 必要な情報が1つの関数内で揃うため、後続のデータ変換処理に必要なパラメータを容易に生成できる。
- **メリット**:
  - トランザクション制御が容易になり、一貫性を保てる。
  - Step Functionsの設計がシンプルになる。
- **デメリット**:
  - 処理が複雑になり、実行時間が長くなるリスクがある。

#### **案2: JSONデータを拡張する**
- データ登録処理からセンサー解析処理に渡されるJSONデータに、`sample.json`の情報をすべて含める。
- センサー解析処理で`sample.json`の情報とセンサー情報を組み合わせてデータ変換処理のパラメータを生成する。
- **メリット**:
  - 現在の分散処理の構造を維持できる。
- **デメリット**:
  - JSONデータが巨大化し、処理負荷が増加する可能性がある。

#### **案3: 専用のLambda関数を作成する**
- データ登録処理とセンサー解析処理をそのままにして、両方の処理が完了した後でデータ変換処理を起動する専用のLambda関数を作成する。
- この専用関数がデータベースから必要な情報を取得し、パラメータを組み立ててデータ変換処理を起動する。
- **メリット**:
  - 現在の構造を大きく変更せずに対応可能。
  - 各処理が分離されているため、保守性が高い。
- **デメリット**:
  - Step Functionsの設計が複雑化し、管理が難しくなる。

---

### 実際に採用した解決策

最終的には、**案1: Lambda関数を統合する** を採用しました。

理由は以下の通りです:
1. **トランザクションの一貫性**が確保できるため、処理の信頼性が向上する。
2. Step Functionsの設計をシンプルに保てる。
3. 実行時間が15分を超えないように設計可能と判断した。

---

### 実装のポイント

#### 1. データ登録とセンサー解析の統合
- **データ登録処理**:
  - `sample.json` をS3から取得し、データをデータベースに登録。
- **センサー解析処理**:
  - メッセージリストを用いてネットワーク名を推定し、データベースに登録。
- 両方の処理を1つのLambda関数にまとめることで、必要な情報を1つの関数内で揃えられるようにしました。

#### 2. トランザクション制御
- Lambda関数内でトランザクションを利用し、すべての処理が成功した場合のみコミットする仕組みを採用。
- 途中でエラーが発生した場合は、すべての変更をロールバックするようにしました。

#### 3. データ変換処理の起動
- データ登録とセンサー解析が完了した後、Step Functionsの次のタスクとしてデータ変換処理を起動。

---

### 結果と振り返り

この統合により、以下の課題が解消されました:
- 必要な情報が1つのLambda関数内で揃うようになり、パラメータ生成が容易になった。
- トランザクション制御が可能になり、中途半端な状態がデータベースに残らなくなった。

ただし、Lambdaの実行時間が長くなるリスクには引き続き注意が必要になります。そのため、処理が重くなりそうな場合は、Step Functionsやバッチ処理をさらに活用する方法も検討しています。
【PowerShell】指定したフォルダ内の画像を縮小して別のフォルダに出力する

大きすぎる画像をあるサイズ以下まで縮小したい、ということがしばしばある。
作業の頻度が少ないうちはペイントでやったりオンラインで圧縮できるサイト等を使ったりしていたが、頻度が多くなってきたためGPT-4君にPowerShellで組んでもらうことにした。
以下はそのコードに少し手直したもの。

```PowerShell:ImageResizing.ps1

# ===設定値===
$scalingFactor = 0.5      #画像縮小率
$inputDir = "C:\Input"    #入力元フォルダ
$outputDir = "C:\Output"  #出力元フォルダ
$usePngFormat = $false    #出力形式（TrueでPNG、FalseでJPEG）

# ===コード===
# 指定フォルダ内の全てのPNGファイルを取得
$imageFiles = Get-ChildItem -Path $inputDir -Filter *.png

# 取得した画像を一枚づつ処理
foreach($imageFile in $imageFiles) {

    # 元画像の読み込み
    $image = [System.Drawing.Image]::FromFile($imageFile.FullName)

    # 新しいサイズを計算
    $newWidth  = [int]($image.Width * $scalingFactor)
    $newHeight  = [int]($image.Height * $scalingFactor)

    # 新しいサイズでビットマップオブジェクトを生成
    $newImage = New-Object System.Drawing.Bitmap $newWidth, $newHeight
    $graphics = [System.Drawing.Graphics]::FromImage($newImage)

    # 描画品質を設定
    $graphics.CompositingQuality = [System.Drawing.Drawing2D.CompositingQuality]::HighQuality
    $graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
    $graphics.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality

    # 画像の描画
    $graphics.DrawImage($image, 0, 0, $newWidth, $newHeight)

    # PNGまたはJPEGで画像を保存
    if($usePngFormat){
        $outputFile = Join-Path $outputDir "$($imageFile.BaseName).png"
        $newImage.Save($outputFile, [System.Drawing.Imaging.ImageFormat]::Png)
    }Else{
        $outputFile = Join-Path $outputDir "$($imageFile.BaseName).jpg"
        $newImage.Save($outputFile, [System.Drawing.Imaging.ImageFormat]::Jpeg)
    }

    # リソースの解放
    $graphics.Dispose()
    $newImage.Dispose()
    $image.Dispose()

    # 処理状況の表示
    Write-Host "Pocessed: $($imageFile.FullName) -> $outputFile"

}

```

エラーハンドリングは入れていないが、個人で使う分には十分使えている。

なお、筆者の場合はPNG指定にしているが、JPEG等も読み込ませたい場合は指定フォルダ内からの取得処理を以下のように書き換えることで対応できる。


```PowerShell:PNG以外も読み込みたい場合
$imageFiles = Get-ChildItem -Path $inputDir -Include *.png, *.jpg -File
```
【Rails】8.0.0からHashWithIndifferentAccess#stringify_keysがハッシュ以外も文字列にするようになった

# 概要
元々あったコードを8.0.0に持って行ったらエラーになったので共有です

8.0.0で`HashWithIndifferentAccess#stringify_keys`の挙動が変更されました

# 内容

`HashWithIndifferentAccess`とは、その名の通り無差別にアクセスできるハッシュのこと

どういうことかというと
```rb
{ name: "Boku", "age": 1 }
```
というHashがあった時、

```rb
musabetsu = { name: "Boku", "age": 1 }.with_indifferent_access
```

とすると

```rb
puts musabetsu[:name]
# => Boku

puts musabetsu["name"]
# => Boku
```
<b>ハッシュ</b>でも<b>文字列</b>でもアクセスできるようになる

ただのハッシュだと、当然keyに登録していない方はnilが帰ってくる
```rb
musabetsu_zyanai = { name: "Boku", "age": 1 }
puts musabetsu_zyanai[:name]
# => Boku

puts musabetsu_zyanai["name"]
# => nil
```

ここで、ここでハッシュのkeyをシンボルでも文字列でもないものにしてみる
```rb
koreha_dou = { 1 => "suji ha?" }.with_indifferent_access
```

ここで8.0.0とそれ以前で<b>挙動が変わってくる</b>

```rb
# 8.0.0以前
puts koreha_dou
{ 1 => "suji ha?" }


# 8.0.0以降
{ "1" => "suji ha?" }
```

社内ライブラリなどで、数字がkeyに入る可能性がある場合は注意してみてください


# リンク
変更PR

https://github.com/rails/rails/pull/53125
Raspberry Pi PicoでBME280を使う

## やってること
温湿度気圧センサBME280で気温、湿度、気圧を計測し、表示させる。

## 環境
マイコン：Raspberry Pi Pico
プログラミング言語：MicroPython (Thony)
温湿度気圧センサ：[BME280](https://akizukidenshi.com/catalog/g/g109421/)

## ライブラリのインストール
Picoへ事前に以下ライブラリをインストールしておいてください。
Picoを接続→左上の欄のツール→パッケージを管理→「micropython_bme280」で検索→インストール

## 接続
| Pico | BME 280 |
|:-:|:-:|
|GP0|SDI|
|GP1|SCK|
|3v3(out)|VDD|
|GND|GND|

### 確認
以下のプログラムを実行して、BME280が正しく接続されているか確認
```py
import machine
sda=machine.Pin(0)
scl=machine.Pin(1)
i2c=machine.I2C(0,sda=sda, scl=scl, freq=400000)
print(i2c.scan())
```
```py
[118]
```
と表示されればOK。されなかったら接続を再確認。

## プログラム

```py:bme280.py
from machine import Pin, I2C
from time import sleep
from bme280 import BME280

i2c = I2C(0, sda = Pin(0), scl = Pin(1), freq = 40000)
bme = BME280(i2c = i2c)

while True:
    print(bme.values)
    sleep(1)
```

## 終わり
他に、各要素（温度、湿度、気圧）だけを表示させるプログラムもあります。
また、地上の気圧から相対高度を計算するプログラムもあります。必要でしたらコメントください。何かご質問等があればとりあえず聞いてください。
2024/12/12 spigotMCを使用したminecraftサーバーを立てる！

# 経緯
友人から急にマイクラサーバーを立ててほしいという旨のメールが来ました。いつも通りpaperMCで立てようと思ったら、どうやらトラップなどの一部動作がspigotを使用しないとうまくいかないらしいので、今回はspigotMCを使用してサーバーを立ててみる。

# 環境
・windows11

# Java23をインストール
ビルドツール&サーバー実行でjavaを使用するのでインストールをする。今回は最新版である1.21.4をインストールするのでJava23をインストールする。

https://www.oracle.com/jp/java/technologies/downloads/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716513/34ed79b7-c393-20a2-4370-bd44af124958.png)

インストーラーがダウンロード出来たら実行し、インストールを完了させる。


# BuildToolsをダウンロードし、ビルドする。
spigotMCでサーバーを立てる際、著作権関係でサーバーから直接ダウンロードはできない。なので、ビルドという手順が必要となる。ビルドとは言っているものの、ただのサーバーダウンローダーと考えて差し支えない。

### BuildToolsのダウンロード
BuildTools.jarを押してダウンロードする。

https://hub.spigotmc.org/jenkins/job/BuildTools/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716513/3d57c0b5-0886-e7c6-f588-d6a14abcf81f.png)

### BuildToolsでビルド
ダウンロードしたBuildTools.jarがあるフォルダでコマンドラインを開き、以下のコマンドを入力する。
```cmd
$ java -jar BuildTools.jar --rev 1.21.4
```
ビルドは結構時間がかかるので気長に待ちましょう。私の実行環境では15分ほどかかりました。

## ビルドしたサーバーを実行する
ビルドが完了すると

**spigot-1.21.4.jar**

というファイルが生成されます。このファイルを別のフォルダに移し、実行するためのbatファイルを作成します。

次のようなbatファイルをspigot-1.21.4.jarがあるフォルダに作成します。
```bat:start.bat
@echo off
java -Xms4G -Xmx4G -jar spigot-1.21.4.jar nogui
pause
```
現在のフォルダ構成は以下のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716513/fc7b2cd2-7e65-1e41-ab56-8cebd357e4f9.png)

batファイルをダブルクリックして実行します。
最初は必ずエラーとなって終了するのでEnterを押して一度閉じます。
このエラーはEULAに同意していないために発生したものです。
EULAに同意するため、新しく生成されたeula.txtを編集します。
eula=trueに変更します。

```diff:eula.txt
#By changing the setting below to TRUE you are indicating your agreement to our EULA (https://aka.ms/MinecraftEULA).
#Thu Dec 12 22:37:21 JST 2024
- eula=false
+ eula=true
```

再びstart.batを実行するとサーバーが起動します。

# 参考文献
https://riminosu13.hatenablog.com/entry/2020/08/13/231056#google_vignette
Ruby on Rails こんなときどうする集

# はじめに
この記事は [DENSO アドベントカレンダー 2024](https://qiita.com/advent-calendar/2024/denso) の13日目の記事です。

社内でRuby on Railsの研修を担当したことがあり、そのときにRuby on Railsで用意されているコマンドについて説明したのですが、「このコマンドってどうなるんだっけ？」というよりは「新しくController作りたいんだけど、なんてコマンド打てばいいんだっけ？」とかの質問が多かったので、こんなときどうする集をまとめてみました。

# こんなときどうする？
但し書きがない限り「コントローラー名」「モデル名」と書いてある箇所は複数形のスネークケースです

## 新しいRailsアプリを作りたい
### コマンド
```
rails new アプリケーション名
```

### 生成物
- アプリケーション名と同名のフォルダ
- (その配下に) railsに最低限必要なフォルダやファイル

## railsサーバを起動したい
### コマンド
```
rails s [オプション]
```
オプション詳細は[こちら](https://railsdoc.com/page/rails_server)

### 生成物
特になし

## railsサーバのコンソールにアクセスしたい
### コマンド
```
rails c
```
もとのコンソールに戻るには`exit`
### 生成物
特になし

## 新しいコントローラを作りたい
### コマンド
```
rails g controller コントローラ名(パスカルケースでも可) [アクション名] [オプション]
```
【注意点】
すでに同名のコントローラがある場合は、実行不可

### 生成物
- `app/controllers/コントローラ名_controller.rb`
- `config/routes.rb`に `get "url" => "コントローラ名#アクション名"`
- `app/views/コントローラ名のフォルダ`
- (その配下に)`アクション名.html.erb`
- テストとヘルパーファイル
【注意点】
設定によって生成物が変わります。

## 既存のコントローラにビューを追加したい
### コマンド
なし
手動で下記を実施
- `config/routes.rb`にルート追加
- 既存コントローラーにアクションを追加
- 既存コントローラーのディレクトリに新しいアクションのビューファイルを追加

## Railsアプリにデータベースを作成したい
### コマンド
```
rails db:create [RAILS_ENV=環境(development, test, production)]
```
【注意点】
すでにデータベースがある場合は、実行不可
### 生成物
- データベース(データベース名はアプリケーション名_環境名)

## 新しいモデルを作成したい (データベース内に新しいテーブルを作成したい)
### コマンド
```
rails g model モデル名 [カラム名:データ型]
# 生成されるマイグレーションファイルを編集してから
rails db:migrate
```
### 生成物
- マイグレーションファイル
- `app/models/モデル名.rb`
- テストファイル


## 既存のモデルを変更したい (データベース内の既存テーブルを変更したい)
### コマンド
```
rails g migration マイグレーション名 [カラム名:データ型]
# 生成されるマイグレーションファイルを編集してから
rails db:migrate
```
### 生成物
- マイグレーションファイル

## 最新のマイグレーションファイルの内容を取り消して、１つ前に戻したい
### コマンド
```
rails db:rollback
```
### 生成物
なし
SELECT FOR UPDATEで行ロックのリトライ

SELECT FOR UPDATE文のタイムアウトを指定し、タイムアウトが発生した場合にtry-catchブロックを使用してリトライする。

1.SELECT FOR UPDATE文のタイムアウト時間を指定する
```sql
SELECT * FROM your_table
WHERE your_condition
FOR UPDATE WAIT 1;
```
2.リトライロジックを実装する
PessimisticLockingFailureExceptionが発生した場合、最大3回までリトライし、リトライ間隔は0.5秒を設定する。
```java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.PessimisticLockingFailureException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
@Service
public class YourEntityService {
 
    @Autowired
    private YourEntityRepository repository;
 
    @Transactional
    public void updateEntity(Long id, String newData) {
        int maxRetries = 3; // 最大3回までリトライ
        int retryCount = 0;
        boolean success = false;
 
        while (!success && retryCount < maxRetries) {
            try {
                YourEntity entity = repository.findByIdForUpdate(id);
                if (entity != null) {
                    entity.setData(newData);
                    repository.save(entity);
                    success = true;
                }
            } catch (PessimisticLockingFailureException e) {
                retryCount++;
                if (retryCount >= maxRetries) {
                    throw e; // リトライ回数を超えた場合は例外を再スロー
                }
                try {
                    Thread.sleep(500); // 0.5秒待機してからリトライ
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("Thread was interrupted", ie);
                }
            }
        }
    }
}
```
【備忘録】DBUnitで登録用レコードの値に改行を入れる

## DBUnitについて
DBUnitでは、登録したいデータをXMLファイルへ記載して
setUp時に登録、tearDown時に削除を行うことができます。

## 改行の入れ方
登録データに改行を入れたい場合、改行文字「\r\n」を
直接入力したり、XML内で改行させたりすることでは、
サニタイジングされて半角スペースに置き換わり、
改行を実現することができません。

対処法として、`&#13;&#10;`を使用することで改行を
入れることができます。

```xml:setUp.xml
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <table_name col1="1行目&#13;&#10;2行目" col2="1行目&#13;&#10;2行目&#13;&#10;3行目" />
</dataset>
```

また、ReplacementDataSet.addReplacementSubstringによって
`&#13;&#10;`を後から埋め込むことでは改行ができませんでした。

### 参考ページ
https://stackoverflow.com/questions/2780902/how-to-insert-line-breaks-dbunit-dataset
Rubyの多重代入における*variable記法

## この記事について

この記事は[Ruby Advent Calendar 2024](https://qiita.com/advent-calendar/2024/ruby)の4日目の記事です。
といっても実は最初は登録していなくて、記事がないことに気づいたので後から埋めるタイプの記事になります。

## 背景

この記事の背景として、[Rubyists Blog Feed Links](https://scrapbox.io/ruby-jp/Rubyists_Blog_Feed_Links)というプロジェクトがあります。これは私が[ruby-jpのCosense](https://scrapbox.io/ruby-jp)内に立ち上げたシンプルなプロジェクトで、Rubyistが書いているブログのフィードをリスト化して最終的にはそれを[OPML](https://opml.org/)にして購読しやすくするというものです。
そのリストに以下のような項目が出てきます。

```
 https://feeds.buzzsprout.com/2399844.rss OH! MY RUBYISTポッドキャストのページ
 ```

 このリストは基本的には`URL name`というフォーマットに則っているのですが、これはnameに空白が含まれています。これだと単純な`split`を使ったときにnameが正しく抽出できなさそうですね？誰なんでしょうね、この項目を追加したのは（私です）。

 ## 主題

上記の項目からURLとnameを得るRubyコードは以下です。

```ruby
url, *name = line.split(" ")
name = name.join(" ")
```

ここで`line`変数にはリストの各行が入ります。そう、たったこれだけでした。

## 解説

`irb`コマンドなどで以下のコードを実行してみてください。

```ruby
a = [1, 2, 3]
```

何が起こるでしょうか。当然、`a`は`[1, 2, 3]`となりますね。
では、以下はどうでしょう。

```ruby
b, c = [1, 2, 3]
```

この場合、`b`は`1`で`c`は`2`になります。Rubyでは多重代入ができるのですが、この書き方だと溢れた値（ここでは`3`）は消えてしまいます。
では、次はどうでしょう。

```ruby
d, *e = [1, 2, 3]
```

ここでは、`d`は`1`ですが、`e`は`[2, 3]`になります。この書き方では`*`付きの変数は配列となり余った値全てを受け取ります。左辺の数が右辺の数より多い場合、`*`付き変数は空配列になります。

### 元々のコード解説

```ruby
url, *name = line.split(" ")
name = name.join(" ")
```

`line`変数は文字列ですので、`split`メソッドは配列を返します。この書き方では、返り値の配列の最初のアイテムが`url`になり、残り（個数不定）が`name`になります。個数にかかわらず`name`は配列になりますので、`join`が呼べます。`name`は多くの場合は`['okuramasafumi']`のように1つの文字列だけが入っている配列ですので、`join`するとその文字列がそのまま取得できますし、`['OH!', 'MY', 'RUBYISTポッドキャストのページ']`のようになっていても`join(" ")`することで復元できます。

## まとめ

実はここに書いたことは[るりま](https://docs.ruby-lang.org/ja/latest/doc/spec=2foperator.html#multiassign)に書いてあります。詳細はそちらをご覧ください。
この記事の目的としては、多重代入における`*`付き変数の実際の利用例を示すことでした。実際にやってみるとわかるのですが、端的に書けるのでかなり気分が良いです。皆さんも機会がありましたら使ってみてください。

## おまけ

ブログをお持ちのRubyistの方は、 https://scrapbox.io/ruby-jp/Rubyists_Blog_Feed_Links へのフィードの追加をお願いします！
データベース・関係の正規化（第1正規形・第2正規形・第3正規形）

# 正規化とは
実績あの事象や情報を矛盾なく管理するために、形式化することやその手順のことを
***正規化***という
正規化したものを**正規形**といい，
条件を満たしていないものを**非正規形**という

# 第1正規形
- 各列が原子値（それ以上分解できない値）である
- 繰り返しの集合を持たない
- 主キーが存在する

## 例1：連絡先データベース
#### 非正規形
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748983/1762ebba-b79b-7792-59ae-f2844f81324b.png)


問題点:
- 電話番号とメールアドレスが複数の値を含む
- データの検索や更新が困難
- 値の数が不定

↓

### 正規形
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748983/a6daf02e-da7a-5328-2352-bd82c53c0237.png)

このように3つのテーブルに分けることで、データの検索や更新・削除が簡単になり、データの整合性やデータ操作の一貫性が確保される。

:::note info
ポイント
- 繰り返しデータを別テーブルに分割する
- 複数の値を含む列を分解する
- 適切な主キーを設定する
- テーブル間の関連を明確にする
ER図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748983/85f213d4-2420-f379-11d6-18e701357487.png)

:::


# 第2正規形 (2NF)
- 第1正規形（1NF）である
- 部分関数従属が存在しない（完全関数従属である）

:::note info
部分関数従属とは？
複合主キーの一部の列だけで他の列が決定される関係のことを指す。 第2正規形では、主キー以外の列は、主キー全体に対して完全に依存している必要がある。
:::


## 例：注文データベース

### 非正規形
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748983/8432aac7-906c-4171-640a-53be4f5b8b0a.png)

問題点:
- 商品名と単価は商品IDのみで決定される（部分関数従属）
- データの重複が発生している
- 更新時の不整合が起きやすい

↓

### 正規形  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748983/4544698b-283b-38fe-5445-959eacfacefa.png)

このように，注文データベースを注文明細テーブルと商品テーブルに分けることでデータの重複が減少し、データの一貫性が向上や更新時の異常が防止、効率的なストレージの使用が可能になる

:::note info
ポイント
- 複合主キーの識別と分解
- 部分関数従属性の除去

テーブル設計のポイント
ER図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748983/d765b59b-1e44-9893-70c7-8eb82fc88045.png)

:::

# 第3正規形 (3NF)
- 第2正規形（2NF）である
- 推移的関数従属が存在しない

:::note info
推移的関数従属とは？
A → B かつ B → C という関係がある場合、A → C という関係が推移的に成立すること。 つまり、主キー以外の列が、他の非キー列に依存している状態を指す。
:::


## 例：書籍販売データベース 

### 非正規形
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748983/cf6ed64c-a68b-e541-ccf9-886355638de5.png)

推移的関数従属の例：
注文ID → 出版社ID → 出版社名、出版社住所


問題点:
- 出版社情報が重複している
- 出版社情報の更新が複数箇所必要
- データの不整合が発生しやすい

↓

### 正規形
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748983/b5e15de7-7368-7236-2a7d-39b0dcc28c6f.png)

このように3つのテーブルに分けることで、データの重複が最小限に抑えられ、データの一貫性が確保でき、データの更新が容易になり、効率的なストレージの使用が可能になっている。

:::note info
ポイント
- 推移的関数従属を見つける
- 適切にテーブルを分割する
- 外部キーを使用して関連付ける
- インデックスを適切に設定する

ER図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748983/216665fc-c707-09f0-f6c1-8163deb30518.png)

:::
【PowerApps】アプリから予定を登録する際に参加者を指定する(予定を共有する)

https://qiita.com/usagikobo2024/items/c60a565310fd633da839

↑こちらの記事でアプリから予定を登録・更新・削除する方法について書きました。

参加者を指定する方法は以下

<iframe width="560" height="400" src="https://www.youtube.com/embed/UqQidbWwDhg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
動画でも解説しています


Office365Outlook.V4CalendarPostItemかPatchItemの中のオプション{}内に記載します。
必須参加者を指定する場合は、requiredAttendees:を入れて、メールアドレスで参加者を指定します。
```javascript
        {
            isAllDay:CheckboxAllday.Value,//終日
            body:RichTextEditorBody.HtmlText,//予定の詳細
            requiredAttendees:Concat(ComboBoxAtt.SelectedItems,Value,";")//必須参加者
        }
```
複数参加者に登録する場合は、メールアドレスを;でつなげて１つの文字列にします。
↑の例ではConcat関数の第1引数にコンボボックスの選択したアイテム(メールアドレス)
第2引数にValue、第3引数に区切り文字の「；」を指定して、選択した複数のメールアドレスを１つの文字列にしています。

```javascript
        {
            isAllDay:CheckboxAllday.Value,//終日
            body:RichTextEditorBody.HtmlText,//予定の詳細
            optionalAttendees:Concat(ComboBoxAtt.SelectedItems,Value,";")//任意参加者
        }
```
↑任意参加者の場合は、optionalAttendees:でメールアドレスを指定します。


Office365Outlook.V4CalendarPostItemの数式全体は以下
```javascript
Office365Outlook.V4CalendarPostItem(
        DropDownBox_Calendar.Selected.Name,//選択した予定表のID※IDだけどNameで取得
        TextInputBox_title.Text,//件名＊テキスト形式
        _start_time,//開始時刻＊テキスト形式で"yyyy-mm-ddThh:mm:ss"
        _end_time,//終了時刻＊テキスト形式で"yyyy-mm-ddThh:mm:ss"
        "(UTC+09:00) Osaka, Sapporo, Tokyo",//タイムゾーンの指定
        {
            isAllDay:CheckboxAllday.Value, //終日予定の場合はtrue
            body:RichTextEditorBody.HtmlText //予定の詳細・本文 ＊HTMLかテキスト形式
            requiredAttendees:Concat(ComboBoxAtt.SelectedItems,Value,";")//必須参加者
        }
    )
```

こちらの記事も参考にしてください

https://qiita.com/usagikobo2024/items/c60a565310fd633da839
Bun+ReactのプロジェクトにPrettier設定を入れる

# はじめに

Prettier は、コードフォーマッターとして有名なツールで、コードのスタイルを統一し、読みやすくすることができます。
チームで開発する際は、必ず初期の段階で入れるようにしましょう。

# 環境

以下で構築した環境を使用しています。 (Bun + React + Vite)

https://qiita.com/grhg/items/dadf53218bb75b172c75


# 手順

早速、公式サイトの手順に沿って、prettier を導入していきます。

https://prettier.io/docs/en/install.html

## インストール

以下のコマンドを実行します。

```bash
bun add --dev --exact prettier
```

これで Prettier がプロジェクトに追加されます。

## 基本設定

プロジェクトのルートに `.prettierrc.yaml` ファイルを作成して、設定を記述します。

```yaml
trailingComma: 'es5'
tabWidth: 2
semi: false
singleQuote: true
```

:::note
JSON等他の形式も対応していますので、その辺りはお好みで。
:::

### 各オプションの説明
- `semi`: 行末にセミコロンを付けるかどうか
- `singleQuote`: シングルクォートを使用するか
- `trailingComma`: 配列やオブジェクトの末尾にカンマを付けるか (es5はES5で有効なカンマをつける)
- `tabWidth`: インデントのスペース数
- `printWidth`: 1行の最大文字数

:::note
詳細は公式サイトを参照してください
https://prettier.io/docs/en/options.html
:::

## (不要) 除外設定

https://prettier.io/docs/en/ignore.html

`.prettierignore` ファイルを作成ると、フォーマットファイルから除外するファイルを指定できます。
しかし、バージョンコントールのディレクトリ (`.git`など) や `node_modules` 、 `.gitignore` に記載されているものはデフォルトで除外されます。
今回は必要なさそうなのでスキップします。

## スクリプトの追加
`package.json` にフォーマット用のスクリプトを追加します。

```diff_json
  "scripts": {
    "dev": "bunx --bun vite",
    "build": "tsc -b && bunx --bun vite build",
    "lint": "eslint .",
+   "format": "prettier --write .",
+   "format-check": "prettier --check .",
    "preview": "bunx --bun vite preview"
  },
}
```

- `format`: プロジェクト全体をフォーマットする
- `format-check`: チェックのみを事項する

## (不要) ESLint の設定追加

以前は、ESLint 側にPrettierと競合する無効化する設定が必要でしたが、`ESLint v8.53.0` からフォーマット関連のルールがdeprecateになり、必要なくなったようです。

https://eslint.org/blog/2023/10/deprecating-formatting-rules/

## フォーマットの実行

以下のコマンドを使用してフォーマットを実行します。

```bash
bun run format
```

無事にフォーマットが実行されました。
```bash
$ prettier --write .
.prettierrc.yaml 11ms (unchanged)
eslint.config.js 28ms
index.html 16ms (unchanged)
package.json 1ms
README.md 24ms (unchanged)
src/App.css 19ms (unchanged)
src/App.tsx 39ms
src/index.css 4ms (unchanged)
src/main.tsx 4ms (unchanged)
src/vite-env.d.ts 1ms (unchanged)
tsconfig.app.json 4ms (unchanged)
tsconfig.json 0ms (unchanged)
tsconfig.node.json 1ms (unchanged)
vite.config.ts 1ms (unchanged)
```

例として、`src/App.tsx` を見ると、以下のように整形されているのが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3294382/59620053-594d-ad1b-d4dc-f4dab82f9930.png)


# まとめ

本記事では、プロジェクトで Prettier を導入しました。
この辺りをやるのも久しぶりなので、記事を残しながら実施してみました。
Web Workers概略

## Web Workersとは
- `message`イベントでハンドル
- UIをブロッキングせずに別スレッドで処理
- ページに依存して終了

### `postMessage`で値を渡す
```javascript
worker.postMessage({type: 'initialLoaded', resText: 'Loaded!!!!'})
```
これは、コールバック内のdataにバインディングされる
```javascript
(event) => {
  console.log(event.data.type); // initialLoaded
  console.log(event.data.resText); // Loaded
}
```

## コード
### コンストラクト
```javascript:index.html
worker = new Worker(`worker.js`, {type: 'module'})
```
### messageイベントに対しコールバック追加
```javascript:index.html
worker.addEventListener("message", (event) => {})
```

worker.js側は、selfがWorkerインスタンス
```javascript:worker.js
self.addEventListener("message", (event) => {})
```

### message
```javascript
worker.postMessage({type: 'initialLoaded', resText: 'Loaded!!!!'})
```

## 記述のコツ
`postMessage`でtypeという共通のpropertyを渡し、typeごとにswitch/caseする。

```javascript:index.html
let worker;
function onLoad(event) {
  worker = new Worker("worker.js", { type: "module" }); // { type: "module"}でES moduleとして読み込み 
  worker.addEventListener("message", (event) => {
    switch (event.data.type){
      case "finishedCalculation":
        console.log(event.data.calcResult);
        break;
      default:
        console.error(`Unknown message '${event.data.type}'.`);
    }
  });
  worker.postMessage({type: "loaded", loadText: "worker"})
}
window.addEventListener("DOMContentLoaded", onLoad, false);

function handleClick(event) {
  if (!worker) return;
  worker.postMessage({type: "calculationStart"});
};
```

```javascript:worker.js
self.addEventListener("message", async (event) => {
	switch (event.data.type) {
    case "loaded":
      console.log(`Hellow, ${event.data.loadText}!`);
      break;
    case "calculationStart":
      await new Promise(resolve => setTimeout(resolve, 2000));
      self.postMessage({
        type: "finishedCalculation",
        calcResult: Math.random().toString()
      });
      break;
    default:
      console.error(`Unknown message '${event.data.type}'.`);
  }
});
```
WSLにおけるCUDAのインストール 2

# この記事について
WSL環境下で、CUDAをインストールする方法その2(備忘録)
Tさん直伝

# 環境
Windows11
WSL2 Ubuntu

# CUDAのホームページからCUDAをダウンロード
## 設定は次のようにする
![スクリーンショット 2024-12-12 22.11.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513172/229b8a58-9c00-873c-3a94-caf73ce87c75.png)

## CUDAのホームページにあるコードを走らせる

```shell:sh
wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt-get update
sudo apt-get -y install cuda-toolkit-12-6
```

## パスを通す

```shell:sh
export PATH=/usr/local/cuda-12.6/bin${PATH:+:${PATH}}
```
```shell:sh
export LD_LIBRARY_PATH=/usr/local/cuda-12.6/lib64\${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
```         

# ```nvcc --version```で存在を確認
```shell:sh
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2024 NVIDIA Corporation
Built on Tue_Oct_29_23:50:19_PDT_2024
Cuda compilation tools, release 12.6, V12.6.85
Build cuda_12.6.r12.6/compiler.35059454_0
```
が出れば成功。
【Spring Initializr 読解】採用技術スタック特有の資材

[【Spring Initializr 読解】ソースコードの読み始め](https://qiita.com/nimzo6689/items/1a64ffba67eda1125f24) の続編です。

Spring Initializr は start.spring.io と initializr の２つのリポジトリで構成されていましたが、その本体側の initializr のソースコードから読んでいきます。

https://github.com/spring-io/initializr

# 採用技術スタック特有の資材

initializr に関わらず、同じ技術を使っていればどのプロジェクトにおいても置かれるような資材を先にまとめていきます。
この辺りは場数を踏めば特に認知負荷を高めるものではないですが、 OSS のコードを読む際についでで確認しておくと、色々知識の幅が広がって楽しいです。

（未使用）と記載しているのは、そういう機能もあるが initializr では使用していない、という意味です。

## .github/

GitHub関連の設定ファイルを格納するディレクトリです。
- Issue のテンプレートやPull Request のテンプレート（未使用）
- GitHub Actionsのワークフロー定義（いわゆる、CI/CD のパイプライン処理）
- Dependabot を使って依存性があるライブラリのバージョンを検知

initializr の Artifact のデプロイや、GitHub に Release を作成したりしている。
実際に、世に公開するためのデプロイ処理は start.spring.io で行っているから、ここでは initializr というライブラリをリリースしているだけ、といった感じ。

### .mvn/

Mavenのプロジェクト固有の設定を格納するディレクトリです。
- Maven Wrapperの設定（`wrapper/maven-wrapper.properties`）
- プロジェクト固有のMaven設定やプロパティ（未使用）

一般的で、最小限の設定のみがある。

### eclipse/

Eclipse IDE用の設定ファイルを格納するディレクトリです。
- Eclipse固有の設定ファイル（`org.eclipse.jdt.*.prefs`）
- フォーマッター設定（`eclipse-code-formatter.xml`）

VSCode の場合は、`/.vscode` に相当。ただ、IDE 関連の設定ファイルは Eclipse しか用意されていない模様。Spring Project で開発していた STS が当初 Eclipse のみサポートしていたのが背景にあったりするのかな。

### git/

Gitのフック（hooks）スクリプトやGit関連の設定を格納するディレクトリです。
- pre-commit, post-commit などのGitフック

Ruby で書かれている Git フック見たの、初めてかも。
Git for Windows の git-bash は bash か perl しか入っていないから、まず、それ以外で書こうと思ったことがない。

### src/

ソースコードを格納する主要なディレクトリです。

のはずですが、checkstyle 関連の設定ファイルしか存在していない模様。ソースコードはモジュールごとに分割されたディレクトリ配下の `src` に入っている。

### pom.xml

Mavenのプロジェクト設定ファイルです。
- プロジェクトの依存関係
- ビルド設定
- プラグイン設定
- プロジェクトのメタデータ

とりあえず、使用されている技術スタック確認したい場合は、まずこのファイルから、という印象。
このファイルの詳細は次回以降の記事で確認したい。

### mvnw / mvnw.cmd

Maven Wrapperのスクリプトファイルです。
- mvnw: Unix系OS用のシェルスクリプト
- mvnw.cmd: Windows用のバッチファイル

Maven Wrapper のおかげで、mvn を各開発者がセットアップしなくてよい。
あと、`mvnw.cmd` はバッチファイルでありながら、実際は処理の大半を PowerShell で書いているという黒魔術を使っている。いつか、解説記事を書きたい。

### .cfignore

Cloud Foundryにデプロイする際、除外するファイルを指定する設定ファイルです。
- デプロイ時に不要なファイルやディレクトリを指定
- .gitignoreと同様の形式

どこで Cloud Foundry が使われているのか、まだよくわかっていない。

### .gitattributes

Gitリポジトリ内のファイルの属性を定義する設定ファイルです。
- 改行コードの制御
- バイナリ/テキストファイルの区別
- diff/mergeの設定

.bat ファイルがないのに、.bat ファイルの設定しかされていない。謎の存在。

### .gitignore

Gitの対象外とするファイルやディレクトリを指定する設定ファイルです。
- ビルド成果物
- IDE固有の設定ファイル
- ログファイル
- 一時ファイル

一般的な設定のみ。[gitignore.io](https://www.toptal.com/developers/gitignore) や [gibo](https://github.com/simonwhitaker/gibo) で生成したコードをそのままペッって貼ってはいなかった。

### LICENSE.txt

プロジェクトのライセンス情報を記載したファイルです。
- オープンソースライセンスの詳細
- 使用条件や制限事項

Apache License 2.0 でした。
著作権と特許は Spring 側が持つ。コードの使用・改変・再配布は自由。ただし、ライセンス表示すること。

### CODE_OF_CONDUCT.adoc

プロジェクトの行動規範を定義したファイルです。

一般的な内容が書かれていました。要するに、みんな仲良くして、それをする気のない人がいたら、プロジェクト側で追い出せる、ということ。

### CONTRIBUTING.adoc

プロジェクトへの貢献方法を説明したファイルです。
- 開発プロセス
- コーディング規約
- Pull Request手順
- テスト要件

コントリビューションするにあたっての細かい点が説明されている。

### README.adoc

プロジェクトの概要や使用方法を説明したファイルです。
- プロジェクトの説明
- セットアップ手順
- 基本的な使用方法
- 参考リンク

前回の記事で読んだ通り。
LinearLayoutでdividerを表示する

LinearLayoutには要素間にDividerを表示する機能があります。

# 使い方

```xml
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?attr/colorSurfaceContainerLow"
    android:divider="@drawable/divider"
    android:dividerPadding="16dp"
    android:paddingHorizontal="16dp"
    android:showDividers="middle|beginning|end"
    >
    ...
</LinearLayout>
```

この指定で、TextViewを間に3つ入れた場合、以下のような表示になります。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/36f9f338-02e4-fbc0-1304-f2f2b0724bad.png)


`android:divider`
dividerとして表示するdrawableを指定します。
Verticalの場合の高さ、Horizontalの場合の幅はdrawableのIntrinsicWidth/IntrinsicHeightが使われるため、GradientDrawableでsizeを指定していないなど、サイズを持たないDrawableを指定した場合は指定しても描画されません。

`android:showDividers`
デフォルト値はnoneであるため指定しないと描画されません。
middle / begginning / end の論理和で指定します。名前の通り、要素の間、最初の要素の前、最後の要素の後それぞれにDividerを描画するかの指定です。

`android:dividerPadding`
Horizontalの場合の上下、Verticalの場合の左右に指定のpadding分のdividerを描画しないように指定できます。非対称なpaddingは指定できません。

---

LinearLayoutというと、超基本的なレイアウトで、昔からずっと使っていましたが、最近までこんなことができるってことを知りませんでした。
いままでdividerをViewで作って間に挟んでたよ…って人は、私一人では無いはず…

以上です。
GitHubでorganization用のドキュメントの整え方


## はじめに

READMEやドキュメントは既存のチームのサポートや将来的に参画する
新規メンバーのために定期的に整備すべき項目です。

GitHubの機能の中で、Organization向けにできる設定をいくつか紹介します。

## ORganization向けのREADME

手順は以下の通りです

1. `.github-private`というリポジトリを作成
1. `profile`ディレクトリ作成
1. `profile`ディレクトリに`README.md`ファイルを作成


https://docs.github.com/ja/organizations/collaborating-with-groups-in-organizations/customizing-your-organizations-profile#adding-a-member-only-organization-profile-readme


## Discussionによる意思決定プロセスの保存

https://docs.github.com/ja/discussions/collaborating-with-your-community-using-discussions/about-discussions

GitHubにはDiscussion機能があり、要件について、ディレクトリ構造について、議事録などの情報を保存することができます。

もちろんある程度issueで会話することも可能ですが、Closeされたissueから情報を探すのは少し手間であるため、全体方針に関わる大事な議論が始まったらDiscussionで整理することを推奨します。
また、場合によっては会議の議事録等も保存しても良いでしょう。

## Wikiで開発Tipsやプロジェクト方針についての情報などを保存する

https://docs.github.com/ja/communities/documenting-your-project-with-wikis/about-wikis

GitHubのwiki機能は主にドキュメントの保存に有効です。
ただし、マークダウン形式限定のためものによっては扱いづらい部分もあるかと思いますが、
wikiはページの階層構造を作れたりするのでそう言った点でもおすすめです。

もちろん、ドキュメントの管理をGoogle WorkspaceやAttlasianに集約しても問題ありません。
例えば、プロジェクト全体に関するドキュメントは上記のようなサービスで保存をし、開発するエンジニア向けにはwikiを使うといったことも有効な使い方だと思います。

ただし、Discussion, Wikiの欠点としてOrganizationに直接設定できないです。
必ずどこかのリポジトリ上に作るしかありません。

そのため、
- それ用にリポジトリを作ってしまう
- 該当リポジトリのwiki, discussionに作成し、導線をOrganizationのREADMEに記載する

と言った手段が必要になります。

ただし、「開発コードとそれに関する情報が近い場所にある」ということは大切なことであるため多少煩わしさの中で扱う価値もあると思います。


以上がGitHubにドキュメント等を集約させるアイディアになります。
他にも良い機能があったら後日追記させていただきます！
GAS(Google Apps Script)で生成AIWebアプリ

## 概要

ごくごく小規模な生成AIWebアプリを開発するのに、GASを使ってみた記録です。

生成AIをWebで活用するにはサーバーサイドスクリプトが欲しくなると思います。APIキーを見えなくしたいからです。Python Flaskとかでも良いけどデプロイ先を考えるのが面倒くさいと思っていたところ、GASでWebアプリが作れることを思い出したので試してみました。

### できたもの

[Words Avenue](https://script.google.com/macros/s/AKfycbwijYFF-CFsyZCmuIpyqDQrn12ZA1tqbUe6snlwPyMLeHuRsMQDmIyvcmgps1-fbig/exec)

![スクリーンショット 2024-12-12 22.12.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92027/10ea5016-52a3-00d7-eaee-b412af771e77.png)

送信されたワード(文章でもいいです。140字まで)を25カ国語くらいに翻訳して一気に表示するサイトです。

ローディング画面を作るのをサボったため、送信後の間がちょっと不安になります…。

あと `phonics` という名前で発音を表示してもらおうと思ったのですが、生成具合が不安定です。

## 使ったもの

### Clasp

ClaspはGAS上のファイルとかをローカルに pull したり、逆にサーバー上に push したりするライブラリです。

GASの画面上のエディターで編集するのは大変なので導入したほうが良いです。

### Rollup / Babel

RollupとBabelを用いて ts ファイルからデプロイ用の js ファイルをビルドしました。

これにより node のモジュールを一緒にデプロイできます。

ただし、今回インストールしたモジュールは @google/generative-ai のみで、それも SchemaType しか使わなかったので、別にバンドルしなくても良かったかもしれません。

### Gemini

Googleで完結しようと思ってAIモデルはGeminiを選択しました。

無料枠があります。すごい！

gemini-2.0-flash-exp を使用しました。

### Almond.css

HTMLファイルのスタイリングに使用しました。クラスレスなタイプのCSSで、HTMLのネイティブなエレメントにスタイルが当たります。

## 詰まったところ

### doPost編

GASではPOSTを受けるのにdoPost()という関数を使います。

```js
function doPost(e) {
  console.log("event", e);
  const sentence = e.sentence !== undefined ? e.sentence : "hello, world!";
}
```

こんな感じで始めましたが、サーバー上でのテストでリクエスト(eの中身)を設定する方法がいまいち分からなかったです。

そこでローカルからcurlでPOSTリクエストをしようと思いましたが、 **CORS** エラーが出てきて失敗…

結局上記 "hello, world" を決め打ちでテストしました。

### fetch編

@google/generative-ai は内部的にfetch APIを使っているみたいなのですが、これがGAS上では動かず。

`UrlFetchApp.fetch` を使って自力でリクエストするように書き直しました。

なので @google/generative-ai で使っているのは型情報のみです。

### doGet編

GASで静的なHTMLを配置しようと思ったら、ブラウザからのGETリクエストを受けることになるので `doGet()` を使います。

```js
function doGet() {
  return HtmlService.createHtmlOutputFromFile("get.html");
}
```

get.html には簡素なフォームを置き、サブミットイベントが発火したら `doPost()` へのリクエストを自力で投げます(e.preventDefault()使用)

```js
const sendForm = document.getElementById("sendForm");
sendForm.addEventListener("submit", async (event) => {
    event.preventDefault();
    // フォーム送信処理
    try {
          google.script.run
            .withSuccessHandler((data) => {
              console.log(data);
            }).doPost({sentence: sentenceVal})
        }catch(e){
            console.log(e)
        }
        
}
```

HTMLネイティブのフォーム送信処理ではなく、 `google.script.run.withSuccessHandler` を使うことが必要だったみたいでけっこう時間を取られました。

## 一応できた

もう一度載せますが、 [Words Avenue](https://script.google.com/macros/s/AKfycbwijYFF-CFsyZCmuIpyqDQrn12ZA1tqbUe6snlwPyMLeHuRsMQDmIyvcmgps1-fbig/exec) です。

カスタムドメインが使えたらな、というのと、

静的HTML(get.html)は本当にただのHTMLなので 久々に Vanilla な JS を書いてDOM操作をしました。

そのあたりで大規模なアプリにはなりようがない気がしますが、小規模な実験的アプリならGASでやるのもありじゃないかなと思いました。
Prompt Injection labに挑戦してみる

お疲れさまです。みやもとです。

[先日参加したイベント](https://qiita.com/Mymt_aggw2208/items/29db0b6e19bef5790007)で、LLMハッキングのお話をうかがいました。
怖くも興味深いお話だったのですが、こういう話を聞くとちょっと試してみたくなる人も居るのではないでしょうか。
私もちょっと試したくなったので、[Prompt Injection lab](https://prompting.ai.immersivelabs.com/)の問題に挑戦してみました。

# プロンプトインジェクションとは
念のため、プロンプトインジェクションについて少しだけ。
検索したところIBMのサイトに記述がありました。

>プロンプト・インジェクションは大規模言語モデル（LLM）に対するサイバー攻撃の一種です。ハッカーは、悪意のある入力を正当なプロンプトとして偽装し、生成AIシステム（GenAI）を操作して機密データを漏洩させたり、誤った情報を拡散させたり、さらに悪い事態を引き起こしたりします。

[IBM 「プロンプト・インジェクションとは」](https://www.ibm.com/jp-ja/topics/prompt-injection)より引用

チャットのやり取りの中でシステム管理者が意図しない動きをさせるための命令を混ぜ込んでしまう等の攻撃ですね。
イベントでは具体的な例を実際に実演して見せていただきましたが、APIの情報を聞き出してDB操作するとか実際あったらめちゃくちゃ怖い話でした…。

# Prompt Injection lab
Prompt Injection labは、実際にプロンプトインジェクションを試してみることができるサイトです。

https://prompting.ai.immersivelabs.com/

運営しているImmersive Labsはサイバーセキュリティトレーニングおよび人材育成に特化したサイバーセキュリティ教育プラットフォームを提供する企業のようですね。
英語読むの大変だったのでClaudeに聞きました。

サイトを開くとこんな感じ。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477580/f49439c4-f226-d6b6-d60c-4129da1411ac.png">

ずらーっとサイトについての説明。
「AIボットからパスワードを聞き出してね」という課題で、Level 1からちょっとずつ難しくなるよということのようです。

ところどころDeepL翻訳のお世話になりつつ、さっそく挑戦。

::: note warn
問題・回答の内容には触れず、挑戦した感想だけ書かせていただきます。
気になる方は実際に挑戦してみてください。
:::

結論から言います。
Level 4で詰まりました。

Level 1は最初ということもありやさしい問題だったのですが、Level 2の時点でもう何を聞けば情報を引き出せるのかでかなり迷いました。
Level 3が解けたのは割と偶然だと思います。

ハッキングを試みる側もそれを阻止する側も、「何を聞いたらどんな答えが得られえるか」「その情報を渡さないための制約をどうシステムに実装するか」というのを思いつくところからまず大変そうだなという感想になりました。
うっすら思ってはいたけどハッカーってすごいですね。怖い。
どの端末でも同じフォントを表示させる方法

# はじめに
HP制作をしている駆け出しエンジニアです。
CSSでフォントを指定したのに、PCとスマホで見ると違うフォントが表示されてしまって困った経験はありませんか？

Google Fontsを使うことで、無料で問題を解決できるので、その方法を備忘録として残します📝

# Google Fontsの使い方
[Google Fonts](httpa://www.google.com/webfonts)

1\. 使いたいフォントのページを開き、「Get font」ボタンを押下する。
![スクリーンショット 2024-12-12 21.44.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950670/89fc70fd-c02b-6f22-62f1-9b6946afffd1.png)

2\. 「Get embed code」ボタンを押下する。
![スクリーンショット 2024-12-12 21.45.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950670/a7529e34-8a67-747f-2919-cdd62120c0a0.png)

3\. 「Web」タブ内の「Embed code in the \<head> of your html」の一番最後の行のみコピーする。
![スクリーンショット 2024-12-12 21.50.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950670/f45e2210-c278-8583-07ae-955779a86963.png)


4\. コピーしたコードをHTMLファイルにペーストする。
（私の場合はWordPressでの開発のため、header.phpに置きました。）
```php:header.php
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<html lang="ja">

<head>
    <!-- Required meta tags -->
    <meta charset="<?php bloginfo('charset'); ?>" />
    <title>sample</title>
    <meta name="description" content="検索した時に表示される" />

    <!-- フォント -->
    <!-- ここに追加 -->
    <link href="https://fonts.googleapis.com/css2?family=Zen+Kaku+Gothic+New:wght@300;400;500&display=swap" rel="stylesheet">
    <!-- ここに追加 -->

    <?php wp_head(); ?>
</head>
```

5\. 「Web」タブ内の「Noto Sans JP: CSS class for a variable style」の`font-family`の行のみコピーする。
![スクリーンショット 2024-12-12 22.06.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950670/ed1c8a58-3b9f-f85b-26c5-2dfcbfddec4f.png)

6\. コピーしたコードをCSSファイルのフォントを指定したいタグ内にペーストする。
（私の場合は、すべての文字に反映させたかったので、style.cssのbodyタグ内に入れました。）
```css:style.css
body {
    /* ここに追加 */
    font-family: "Noto Sans JP", sans-serif;
    /* ここに追加 */
    color: #757575;
}
```

この手順を踏むことで、簡単にどの端末でも同じフォントを表示させれることができます!
以上、参考になれば幸いです。
決済負荷試験で学んだOracleDB待機イベントの対処法

# はじめに

はじめまして！
決済業界でエンジニアをしている [@ykagano](https://x.com/ykagano) です！

「[PAY Advent Calendar 2024](https://qiita.com/advent-calendar/2024/payjp-02)」の18日目の記事ということで、3年ほど前に決済の負荷試験で学んだOracleDBの待機イベントの対処法を書こうと思います！

# 決済はいつかスパイクする

決済システムでは決済件数が増えてくるのを見越して、現行システムがどれぐらいのリクエストを処理できるのか負荷試験で確認し、増強計画を立てるキャパシティプランニングを行います。

当時負荷試験を実施したところ、300tpsほどで**処理時間のスパイク（急激な変動）** が発生していました。

■AIで生成したスパイクのイメージ画像

![スクリーンショット 2024-12-12 12.00.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246392/7c31087d-3db1-0906-00aa-f13593a5851e.png)

### 原因

スパイク時にOracleの待機イベントで `buffer busy waits` とともに、 `index contention` が発生していたと思います。

複数のセッションからBツリー索引の同一リーフブロックへの書き込みが行われ、**スプリット待ち（リーフブロックの再編成待ち）** になっている状態でした。

下図は1〜7までのBツリーに8を追加しようとした場合にスプリットが発生するシンプルな例です。

```mermaid
graph TD
    subgraph "Bツリー探索パス"
        1["1"]
        2["2"]
        3["3"]
        4["4"]
        5["5"]
        6["6"]
        7["7"]
        8["8"]

        4--> 2
        4--> 6
        2 --> 1
        2 --> 3
        6 --> 5
        6 --> 7

        %% 8の探索パス
        4 -.->|"8追加"| 6
        6 -.->|"スプリット発生"| 8

        %% スタイル設定
        style 8 fill:#ff9999
    end
```

決済用のテーブルにはINDEXを最小限としていたため、原因になるのは主キーのみでした。

主キーにシーケンシャルな値は使用していなかったのですが、**一部時系列データを使用していたことが原因**でした。

例えば2024年から2025年へ変わる1秒前にデータの書き込みが発生したとします。

「<font color="Red">20241231235959</font>ASDFGH」と「<font color="Red">20241231235959</font>ZXCVBN」という文字列を主キーにINSERTしようとした場合、**先頭からの文字列の大半が同一**であるため、Bツリー索引により同じリーフブロックが更新されてしまう状況でした。

# 対策

文字列全体を分散させることが必要だったため、**主キーに入る値をハッシュ化した値に変更**しました。

```
ハッシュ化前：20241231235959ASDFGH
ハッシュ化後：6F5C6F7F3408F9FC4F91845E6C686B8ED8D61F51082B012F3C8989FD53D5F648
```

そしてハッシュ化前の値は新しく追加したカラムへ入れることにしました。

該当箇所のみですが、下記テーブル定義のイメージです。

### テーブル定義

| カラム名 | データ型 | キー | 説明 |
|---------|----------|------|------|
| id | varchar(255) | PK | 決済ID（ハッシュ化後） |
| ... | ... | - | 中略 |
| payment_id | varchar(255) | - | 決済ID（ハッシュ化前）←カラム追加 |

またバックオフィス向けに、決済情報のデータ取得処理も以下のように変更しています。

### データ取得処理

1. ハッシュ化した値で主キーを検索（ハッシュ値に切り替え後のデータ）
2. ヒットしなければハッシュ化前の値でも主キーを検索（ハッシュ値に切り替え前の過去データ）

ハッシュ化前の決済IDは短い文字列でしたが、ハッシュ化後の決済IDは64文字と表示するには扱いにくいものでした。

そのため、上記1のハッシュ化後の決済IDは検索用とし、**ハッシュ化前の決済IDを表示用に使用する**ようにしました。

下図はデータ取得処理のシーケンス図です。

```mermaid
sequenceDiagram
    participant C as クライアント
    participant S as サービス
    participant DB as データベース

    C->>+S: 決済情報取得リクエスト
    S->>S: 決済IDをハッシュ化
    S->>+DB: ハッシュ化IDで検索<br/>(id = ハッシュ値)
    DB-->>-S: 検索結果

    alt ハッシュ化IDでヒットした場合
        S->>S: payment_idを取得<br/>(ハッシュ化前の決済IDを使用)
    else ハッシュ化IDでヒットしない場合（過去データ）
        S->>+DB: ハッシュ化前の値で検索<br/>(id = 元の決済ID)
        DB-->>-S: 検索結果
    end

    S-->>-C: 決済情報を返却<br/>(ハッシュ化前の決済IDを使用)
```

上記対策後、再度300tpsを超える負荷試験をしても**DBの待機イベントは発生しなくなりました。**

# まとめ

INSERTの多いテーブルのINDEXに、**時系列やシーケンシャルな文字列は一部であっても避けましょう！**

index contention についての詳細な情報はこちらが参考になりました。

https://www.ex-em.co.jp/blog/enq-tx-index-contention/

以上です。
瑕疵担保責任

## 瑕疵担保責任

この記事を見て理解できること
・瑕疵担保責任の意味
・現在の「瑕疵担保責任」
・準委任契約における契約不適合責任


### 瑕疵担保責任とは
瑕疵担保はシステム開発の文脈で使用されたときは以下のような意味があります。
「契約を交わして納品したサービスや製品に不具合が生じ契約の目的に達しなかった場合、発注者側は契約の解除や補修の要求ができるといった意味」

### 現在の瑕疵担保責任
2020年4月1日に法改正があり、瑕疵担保責任の法律が改正され、「契約不適合責任」に変わりました。
契約不適合責任は、請負契約をした仕事の目的物について「種類」「品質」「数量」が契約の内容と合っていない場合に、請負人（受注者）が負う責任のことを指します。つまり、納品した製品やサービスが契約内容と相違していた場合に生じる責任です。

### 準委任契約における契約不適合責任
現在、システムの契約形態は専ら準委任契約が多いです。準委任契約とは、業務の遂行そのものを委託する契約であり、特定の成果物を完成させる業務ではありません。成果物の完成や品質の保証を求める場合は、請負契約に該当します。
準委任契約の場合、成果物に対して責任が無いため基本的には契約不適合責任を負うことはありません。しかし、別の責任として、善管注意義務を負うことになります。善管注意義務とは、専門家としてある程度の知識やレベルを踏まえて業務を遂行する義務です。
これを踏まえると準委任契約の場合は、検収後バグが発生してシステムが止まっても損害賠償を支払わなくても良さそうだと思ってしまいますが、善管注意義務の観点で、専門家としての業務を遂行できていないと判断された場合、損害賠償の責任が発生します。

準委任だからといって気を抜かないように、、、汗