OmniAICreator/Qiita-1.07M huggingface dataset:
React useState更新直後の値参照が引き起こす意図しない挙動とその解決策

## はじめに

Reactで開発をしていると、`useState`でstateを更新した直後にその値を使いたくなることがありますよね。しかし、これは思わぬバグを引き起こす可能性があります。この記事では具体例を通じて、よくある落とし穴とその解決方法を解説します。

## 問題のあるコード例

以下のような商品リストにアイテムを追加する機能を考えてみましょう：

```typescript
const ShoppingList = () => {
  const [items, setItems] = useState<string[]>([]);
  const itemRefs = useRef<{ [key: string]: HTMLDivElement | null }>({});

  const addItem = useCallback(() => {
    const newItemId = generateId(); // ユニークなIDを生成
    setItems(prev => [...prev, newItemId]);
    
    // 追加したアイテムにフォーカスを当てたい
    const lastItemId = items[items.length - 1];
    const element = itemRefs.current[lastItemId];
    if (element) {
      const input = element.querySelector('input');
      input?.focus();
    }
  }, [items]);

  return (
    <div>
      {items.map(id => (
        <div key={id} ref={el => itemRefs.current[id] = el}>
          <input type="text" placeholder="商品名を入力" />
        </div>
      ))}
      <button onClick={addItem}>商品を追加</button>
    </div>
  );
};
```

一見問題なさそうに見えますが、このコードには重大な問題があります。

## 何が問題なのか？

問題は`setItems`による状態更新が即座には反映されないということです。

Reactの`useState`の更新は非同期で行われます。そのため：

1. `setItems`を呼び出しても、その直後の同じスコープ内では`items`の値は更新されていません
2. `items[items.length - 1]`で取得されるのは、新しく追加された項目ではなく、一つ前の古い項目のIDになってしまいます
3. 結果として、意図した要素にフォーカスが当たらない

## 正しい実装方法

この問題は`useEffect`を使用することで適切に解決できます：

```typescript
const ShoppingList = () => {
  const [items, setItems] = useState<string[]>([]);
  const itemRefs = useRef<{ [key: string]: HTMLDivElement | null }>({});
  const previousLength = useRef(items.length);

  const addItem = useCallback(() => {
    const newItemId = generateId();
    setItems(prev => [...prev, newItemId]);
  }, []);

  // itemsの変更を検知して適切なタイミングでフォーカスを制御
  useEffect(() => {
    if (items.length > previousLength.current) {
      const lastItemId = items[items.length - 1];
      const element = itemRefs.current[lastItemId];
      if (element) {
        const input = element.querySelector('input');
        input?.focus();
      }
    }
    previousLength.current = items.length;
  }, [items]);

  return (
    <div>
      {items.map(id => (
        <div key={id} ref={el => itemRefs.current[id] = el}>
          <input type="text" placeholder="商品名を入力" />
        </div>
      ))}
      <button onClick={addItem}>商品を追加</button>
    </div>
  );
};
```

この実装のポイント：

1. アイテムの追加処理とフォーカス制御を分離
2. `useEffect`で`items`の変更を監視
3. 実際に値が更新された後にフォーカス制御を実行

## バグの発見が難しい理由

このようなバグが見つけにくい理由として：

1. 開発環境では処理が早すぎて問題が顕在化しにくい
2. 別の実装（useEffectなど）で意図した動作が実現されている場合がある
3. コードレビューでも見落としやすい

## まとめ

Reactでstateを扱う際の重要なポイント：

1. `useState`の更新は非同期で行われる
2. 更新直後の同じスコープ内では新しい値は反映されていない
3. 更新後の値を使用したい場合は`useEffect`を使用する
4. 状態更新とその後の副作用は分離して管理する

これらの原則を理解していれば、より堅牢なReactアプリケーションを作ることができます。

## 参考リンク
- [React公式ドキュメント：state の更新は非同期](https://react.dev/learn/state-as-a-snapshot)
- [React公式ドキュメント：useEffect](https://react.dev/reference/react/useEffect)
ROS2で多次元配列を送受信する - ndarray_msg -


## 背景
深層学習では、深層モデルから出力される純粋な特徴量はテンソル（多次元配列）として表現されます。

ROS2には画像やポイントクラウドなど、豊富なメッセージ型が用意されていますが、深層学習のような、純粋な多次元配列だけを送受信したいケースもあります。

ということで、今回はNumPyの多次元配列（ndarray）を簡単にROS2ノード間で送受信できるパッケージ **ndarray_msg** を作成しました。

ROS2のDistroは **Humble**と**Jazzy**で検証しています。

## GitHubリポジトリ

パッケージのソースコードは以下のリポジトリで公開しています：

https://github.com/Geson-anko/ndarray_msg

## 使い方

### インストール方法

1. まず、ROS2のワークスペースにパッケージをクローンします：

```bash
cd ~/ros2_ws/src
git clone https://github.com/Geson-anko/ndarray_msg.git
```

2. パッケージをビルドします：

```bash
cd ~/ros2_ws
colcon build --packages-select ndarray_msg
source install/setup.sh
```

3. Python用のユーティリティパッケージをインストールします：

```bash
pip install ros2-ndarray-msg-utils
# or
cd ~/ros2_ws/src/ndarray_msg
pip install .  # uv などを使っている場合は uv add <path/to/ndarray_msg>
```

### 基本的な使用例

NumPy配列をROS2メッセージに変換し、別のノードで受信して元の配列に戻す基本的な例：

```python
import numpy as np
from ndarray_msg_utils import to_ros_msg, from_ros_msg
from rclpy.clock import ROSClock

# NumPy配列をROS2メッセージに変換
array = np.array([[1, 2], [3, 4]], dtype=np.float32)
msg = to_ros_msg(array)

# ヘッダ情報付きでメッセージを作成
msg = to_ros_msg(
    array,
    timestamp=ROSClock().now(),
    frame_id="camera_frame"
)

# メッセージからNumPy配列に戻す
restored = from_ros_msg(msg)
```

### (2025/03/26 追記) PyTorchに対応しました！

次のコマンドで`torch`もインストールします。

```sh
pip install ros2-ndarray-msg-utils[torch]
```

**使用例**

```py
import torch
from ndarray_msg_utils.torch import to_ros_msg, from_ros_msg

# Convert PyTorch tensor to ROS2 message
tensor = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32)
msg = to_ros_msg(tensor)

# Convert back to PyTorch tensor
restored = from_ros_msg(msg)
```



## メッセージの詳細について

```
# NDArray message type for pure numerical data
std_msgs/Header header # header information
string dtype          # numpy dtype name (e.g., 'float32', 'float64', 'int64')
uint32[] shape        # ndarray dimensions
uint32 data_size      # total number of elements
uint8[] data          # serialized ndarray data
```

### 各フィールドの役割：

- `header`: 標準的なROS2ヘッダ（タイムスタンプとフレームID）
- `dtype`: NumPy配列のデータ型名（例：'float32', 'int64'）
- `shape`: 配列の各次元のサイズ
- `data_size`: 配列の全要素数
- `data`: バイト列にシリアライズされた配列データ

## 最後に

とりあえずザッと作った簡易なパッケージですので、プロジェクトにそのままコードを移植して使用しても良いと思います。

何か皆さんのお役に立てば幸いです〜

## 参考文献

* [Creating custom msg and srv files](https://docs.ros.org/en/jazzy/Tutorials/Beginner-Client-Libraries/Custom-ROS2-Interfaces.html)
* [NumPy Documentation](https://numpy.org/doc/stable/)
* [ROS2 Documentation](https://docs.ros.org/en/jazzy/)
Go Modules：基本の使い方と活用方法

## はじめに

Go Modulesは、Go言語でプロジェクトを管理するための依存関係管理ツールです。  
従来の$GOPATHに依存せず、プロジェクトごとに依存関係を管理することができ、モダンなGo開発には欠かせない機能となっています。

---

## Go Modulesとは？

Go Modulesは、以下の特徴を持つ依存関係管理ツールです。

- プロジェクトごとに依存関係を管理できる。
- $GOPATHに依存せずに、任意のディレクトリで開発が可能。
- バージョン管理が簡単に行える。
- 他の開発者と同じ環境を再現しやすい。

### **Go Modulesのメリット**
- プロジェクトごとに依存関係を分離できるため、環境の競合を防げる。  
- 任意のディレクトリで Go プロジェクトを作成 / 管理できる。  
- バージョン管理が容易になり、同じバージョンの依存関係を再現できる。  

---

## Go Modules の基本コマンド

**1. プロジェクトの初期化**

新しいGo プロジェクトを作成する場合、go mod initを使って Go Modulesを初期化します。

```sh
mkdir myproject
cd myproject
go mod init github.com/username/myproject
```

- github.com/username/myprojectは、モジュール名であり、一般的に GitHubのリポジトリパスを指定します。
- 実行後、go.modファイルが生成されます。

**2. 依存関係の追加**

Go Modulesでは、外部パッケージをインポートすると自動で依存関係が追加されます。

例として、`github.com/gorilla/mux` を使ってみます。

```go
package main

import (
    "fmt"
    "net/http"

    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello, Go Modules!")
    })

    http.ListenAndServe(":8080", r)
}
```

依存関係を追加するには、以下のコマンドを実行します。
```sh
go mod tidy
```

- go mod tidyは、go.modとgo.sumを自動的に更新します。
- go.sumファイルには、依存関係のチェックサムが記録され、依存関係の一貫性が保たれます。

---

**3. 依存関係の更新**

依存関係を最新バージョンに更新したい場合は、以下のコマンドを実行します。

```sh
go get -u github.com/gorilla/mux
```

- -uフラグを付けることで、マイナーバージョンの更新を行います。
- 最新のパッチバージョンのみを取得する場合は、-u=patchを指定します。

```sh
go get -u=patch github.com/gorilla/mux
```

---

**4. 依存関係の削除**

不要になった依存関係は、自動的には削除されません。  
以下のコマンドを実行することで、使われていないパッケージを削除できます。

```sh
go mod tidy
```

- go mod tidyは、現在のプロジェクトで使用されていない依存関係を削除し、go.modとgo.sumを整理します。

---

## Go Modules のバージョン管理

**1. 特定のバージョンを指定する**

依存関係のバージョンを指定してインストールすることも可能です。

```sh
go get github.com/gorilla/mux@v1.8.0
```

- @v1.8.0のように、バージョンタグを指定します。
- 特定のコミットを指定することもできます。

```sh
go get github.com/gorilla/mux@<commit-hash>
```

**2. バージョンの確認**

依存関係のバージョンを確認するには、以下のコマンドを使用します。

```sh
go list -m all
```

- go list -m allは、go.modに記載されているすべての依存関係とそのバージョンを表示します。

---

## プロジェクトの実行とビルド

**1. 実行**

Go Modulesを使ったプロジェクトは、以下のコマンドで実行できます。

```sh
go run main.go
```

### **2. ビルド**

バイナリを生成するには、go buildを使用します。

```sh
go build -o myapp main.go
./myapp
```

- `-o` オプションで、生成するバイナリの名前を指定します。

---

## まとめ

| 項目 | 説明 |
|------|------|
| **Go Modules** | プロジェクトごとの依存関係管理ツール。$GOPATH に依存しない。 |
| **go mod init** | 新しいプロジェクトを初期化する。 |
| **go mod tidy** | 依存関係の追加、削除、および整理を行う。 |
| **go get** | 依存関係のインストール・更新・バージョン指定が可能。 |

依存関係の管理が楽になり、モダンな Go 開発が可能になります。
【Salesforce認定Platformアプリケーションビルダー】やったこと

## この記事は
筆者が、2025年2月にSalesforce認定Platformアプリケーションビルダーを取得した際に使用した教材などのまとめです。

### 筆者のスペック
+ エンジニア2年目
+ Salesforce実務未経験
+ これまでに認定アドミニストレーター、認定Platformデベロッパーを取得
    + [【Salesforce認定アドミニストレーター】やったこと](https://qiita.com/shi-mashimashima/items/9217fa657897538b571f)
    + [【Salesforce認定Platformデベロッパー】やったこと](https://qiita.com/shi-mashimashima/items/d3f86fe1b6468b3a68c8)

### 認定Platformアプリケーションビルダー資格概要
#### 試験の詳細
+ 多肢選択式の60問
+ 試験時間は105分
+ 合格点は63%
+ 受験料は30,000円（税抜）
（20,000円だったのに2025/02/01～値上がりしました泣）
#### 試験概要
+ Salesforce の基本(23%)
+ データモデリングおよび管理(22%)
+ ビジネスロジックとプロセスの自動化(28%)
+ ユーザインターフェース(17%)
+ アプリケーションのリリース(10%)

## 勉強の流れ
ステップ1→1週間
ステップ2→1週間
くらいの時間配分で取り組みました。

### ステップ1：Trailmix
認定資格用のTrailmixをこなしていきます。
認定アドミニストレーターと被っている部分も多かったためサクサク進められました。

https://trailhead.salesforce.com/ja/users/welcome4/trailmixes/prepare-for-your-salesforce-platform-app-builder

推奨Superbudgeもありますが取り切れなかったですね…

https://trailhead.salesforce.com/ja/content/learn/superbadges/superbadge_security_specialist

https://trailhead.salesforce.com/ja/content/learn/superbadges/superbadge_process_automation_specialist

https://trailhead.salesforce.com/ja/content/learn/superbadges/superbadge-data-model-optimization-specialist

### ステップ2：問題演習
問題集を周回していきます。
他の試験の時と同じやり方なのですが
+ 1周目：ざっくり解く（わからなくてOK）、初見の用語をスプレッドシートにまとめる
+ 2周目：解説をしっかり読んで頭に入れる
+ 3周目：再度1周して不明点を明らかにする

という流れで進めていきます。

ネットにある問題集を使いますが、どれも時折正解が間違っていたり、問題集によって正解がブレていることがあります。
そのため怪しい問題については、解説に加えて
実際に操作する・ヘルプ読む・ググる・Copilotに投げる
などして検証をしていました。

前回の認定デベロッパー試験では、本番で見たことのない問題が出てパニックになっていました…
そのため今回は、ただ正解を選ぶだけではなく「なぜそうなるのか」まで自分で道筋をつけられるようにしました。
具体的には、ある程度周回が進んだところで
+ 解説を見ずに正解／不正解の理由を考える
+ 選択肢を見ずに問題を解く

といった勉強をしていました。

スプレッドシートにはこんな感じでまとめてました。
答えが間違っている問題に関しては、正答を記録しておくと後の周回で便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3978744/0ec98d60-b358-4b2d-92b9-069a903c17a2.png)

#### 使った問題集
##### Tysonblog
https://tysonblog-whitelabel.com/category/salesforce/salesforce-appbuilder

全247問。無料。
認定アドミニストレーター・認定Platformデベロッパーのときにもお世話になりました。
解説がとにかく丁寧で、不正解の選択肢も「なぜ不正解なのか」がわかります。

##### note「Salesforce認定Platformアプリケーションビルダー試験の対策講座」
https://note.com/salesforce777/n/n09b069e66866

全240問。有料。
こちらも他のSalesforce資格でお世話になっています。
画面付きの解説なので操作のイメージがしやすいです。

##### JPN試験過去問
無料。
問題数がとても多いので最新ぽい以下の二つだけ取り組みました。
翻訳がユルユル&答えが間違っている割合高めなので、上記の2つの問題集を終えてから解き進めるとよいです。

+ 全119問

https://www.jpnshiken.com/shiken/Salesforce.Platform-App-Builder-JPN.v2024-07-21.q119.html

+ 全122問

https://www.jpnshiken.com/shiken/Salesforce.Platform-App-Builder-JPN.v2025-02-04.q122.html

## 受験
### 試験会場
+ オンサイト受験しました。
+ 今回は通信トラブルなどもなく、安心して受験できました。
### 試験内容
+ 回答15分+見直し20分、で35分ほどで終わりました。
+ ほぼ見たことのある問題&選択肢でした。
Tysonblogとnoteに加え、今回からJPN試験の問題集にがっつり取り組んだのが功を奏したようです。
+ 得点率はこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3978744/5ddfa4f6-dbe7-440e-98c5-6c21cc756d01.png)


## 終わりに
+ Salesforceの認定試験にもだんだん慣れてきました。
+ 基本的な資格は一通り取れたかなと思うので、今後は上級の資格やAI関連の資格にも手を広げていきたいです。
エンドツーエンドの暗号化はNLB

## 概要
AWS ANS試験の勉強にあたり、エンドツーエンドの暗号化について理解するための備忘録です。

## なぜ Network Load Balancer (NLB) を選ぶのか？
ALB (Application Load Balancer) は HTTPS ターミネーションを行うため、ALB では「エンドツーエンドの暗号化」は実現できない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3300497/c3ba3015-7d76-4fcb-a8e1-e145d76ecc50.png)

NLB (Network Load Balancer) は TCP レベルで動作し、SSL ターミネーションをしないため、クライアントから EC2 までの完全な暗号化を維持できる。

ALB では クッキーを使ったスティッキーセッション をサポートするが、NLB では ソース IP を使ったスティッキーセッション を設定できる。

TCP レベルの負荷分散でスティッキーセッションを実現するには NLB が適している。

## HTTPS のリクエストはどう処理するのか？

NLB は L4 (Transport Layer) の負荷分散 を行うため、TCP 443 のリクエストをそのまま EC2 に転送するだけ。

EC2 側で SSL/TLS の処理を行うので、エンドツーエンドの暗号化が維持される。

つまり、NLB 自体は HTTPS を「理解」しないが、TCP 443 のトラフィックをそのまま EC2 に渡すため、HTTPS 通信が維持される。

## エンドツーエンドの実装例
1. NLB を作成する
2. ターゲットグループを作成
 プロトコル: TCP
ポート: 443
3. スティッキーセッション (セッションアフィニティ) を有効化
4. EC2 インスタンスをターゲットグループに登録
5. リスナーを作成
プロトコル: TCP
ポート: 443
6. EC2 インスタンスに SSL 証明書をデプロイ
7. EC2 側で HTTPS (SSL/TLS) を終端する
8. エンドツーエンドの暗号化を実現！

## ALBではだめなのか？
ALB を使うと、通常は ALB で HTTPS を終端 して、その後は HTTP で EC2 に転送される。
しかし、要件は「エンドツーエンドの暗号化」なので、ALB では不適切。
ALB に SSL パススルーの機能はない ため、HTTPS を維持するなら NLB を使うしかない。

## まとめ
- HTTPS は「L7 (アプリケーション層) のプロトコル」なので、NLB は「理解しない」が、TCP 443 のトラフィックをそのまま転送できる。
- EC2 側で SSL/TLS を処理することでエンドツーエンドの暗号化が実現できる。
- スティッキーセッション (セッションアフィニティ) を IP ベースで実現するためには NLB が適している。
メールアドレスの最大長は？主要サービスの制限も調査

## はじめに
メールアドレスの最大長はどのくらいなんだろう、と思い調べたところ、RFCにより全体では254文字と定められていることが分かりました。
しかし、そんな長いメールアドレスなんて使ってるやついるか？と思い、よく使われているメールアドレスの最大長を調べてみました。（各メールサービスごとにローカル部分（@の前の部分）の文字数に独自の制限を設けているため、254文字にすることができない）

## よく使われているドメインの最大長

簡単に調べてみました。もしかしたら間違っているかもしれません。リンクもつけておきました。

| メールサービス   | ローカル部分 | ドメイン部分         | 最大長（概算） |
|-------------------|------------------|----------------------|----------------|
| [Gmail](https://support.google.com/mail/answer/9211434?hl=ja)             | 6~30文字           | @gmail.com            | 約40文字       |
| [Yahoo!メール](https://support.yahoo-net.jp/SccMail/s/article/H000010066)      | 4~31文字           | @yahoo.co.jp         | 約40文字       |
| [Outlook](https://answers.microsoft.com/ja-jp/outlook_com/forum/all/%E3%83%A1%E3%83%BC%E3%83%AB%E3%82%A2%E3%83%89/f4f28a47-9947-4998-8abb-f8cb7ed64ab1) | 64文字           | @outlook.com          | 約70文字       |
| [iCloudメール](https://support.apple.com/ja-jp/guide/icloud/mm6b1a490a/1.0/icloud/1.0)      | 3～20文字       | @icloud.com           | 約30文字   |

## おわりに
最大長は254文字にするのが確実ですが、対象が一般的なメールアドレスのみであるならば、100文字程度で全然十分だとわかりました。
逆に、100文字を超えるメールアドレスは、特殊な運用やシステム向けのものである可能性が高いため、一般的な用途では100文字以内に収めるのもいいのかもしれません。
Match rule in Identity Resolutiでのエラー



+ [Get Started with an Identity Resolution Ruleset](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000KDqKN0A1)

:::note
There was also such an error. You just needed to close the window and redo it, and then everything worked. For some actions on Data Cloud/Agentforce PG, a certain amount of time is required to approve those actions.  

こんなエラーもありました。ウィンドウを閉じてやり直したらうまくいきました。Data Cloud/Agentforce PGの一部のアクションは、承認に一定の時間が必要です。
:::


+ [In the trailblazer Bring External Data into the Contact Page Layout module I'm running into an error](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX000000TpQe0AK)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/63b6012b-5eb6-4b0b-b8c0-e850c5f57228.png)


:::note
After the org deleted itself I created a new org, and successfully worked through the trailhead.  The new org worked as expected
:::



+ [Why i am not able to use the Match rule in Identity Resoluti
](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000KDbn70AD)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/72b90114-37e3-48f4-bad0-676303d5788f.png)

:::note
The four types displayed at the top in "Add Match Rules" are merely the default match rules. By selecting the "Custom Rule" at the bottom, if you create a custom rule, are you unable to use "Exact Normalized"? Can "Exact" also not be used at the same time? If an error occurs in this case, I believe it will help narrow down the source of the issue.  
:::

+ [Why i am not able to use the Match rule in Identity Resoluti](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000KDbn70AD)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/abc1d33b-5f34-4bd0-a243-ccc30a810835.png)

:::note
The four types displayed at the top in "Add Match Rules" are merely the default match rules. By selecting the "Custom Rule" at the bottom, if you create a custom rule, are you unable to use "Exact Normalized"? Can "Exact" also not be used at the same time? If an error occurs in this case, I believe it will help narrow down the source of the issue.  

「一致ルールの追加」で上部に表示されている4種類は、あくまでデフォルトの一致ルールです。下部の「カスタムルール」を選択して、カスタムルールを作成すると、「Exact Normalized」は使用できなくなるのでしょうか？同時に「Exact」も使用できなくなるのでしょうか？この場合にエラーが発生すると、問題の原因を絞り込むのに役立つと思います。  
:::
Snowflakeのクエリキャッシュについて理解する

# 1.Snowflakeのクエリキャッシュについての動きを理解する
Snowflakeは、取得したクエリに対して、まずクエリキャッシュを探しに行きます。

![1716051905712.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526914/6b5bbb18-e581-4a44-91b9-b987b43f3db1.png)

引用：
https://www.linkedin.com/pulse/query-lifecycle-snowflake-minzhen-yang-7mbfc/


## ディスクのデータ読み込みの流れ
ローカルディスク（キャッシュ）を確認

Snowflakeの仮想ウェアハウス（コンピュートリソース）が使用するキャッシュにデータがあれば、それを利用。
キャッシュには、最近クエリで使用されたデータのコピーが含まれる。
これにより、頻繁にアクセスされるデータの取得を高速化。
キャッシュにデータがなければ、リモートストレージ（クラウドストレージ）を確認

Snowflakeは、ストレージ層にデータを保存している（AWS S3、Google Cloud Storage、Azure Blob Storage）。
ローカルキャッシュにデータがない場合、ここからデータを取得し、必要に応じてキャッシュに保存。


# 2.クエリキャッシュの種類

Snowflakeには、いくつかのクエリキャッシュ機能が存在します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526914/1d0162c1-c4a4-4547-b822-f70b1dc271a8.png)

## クエリリザルトキャッシュ
クエリリザルトキャッシュについては、以下のように確認することができます。

```sql
select
    *
from
    transactions m
left outer join
    customer_data d1
on
    m.customer_id = d1.customer_id;

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526914/7272e58c-b44f-4003-97f7-6e6614e5f5b3.png)
このように、２つのテーブルスキャンと、ジョインの処理が実際にメモリ上で走っていることがわかります。

また同じように、同一クエリを走らせてみると以下のように、クエリキャッシュが効いていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526914/939afc33-0554-4e79-bfb2-d86f9d73feca.png)

## ウェアハウスキャッシュ

では、このクエリキャッシュが効いた状態で、その中の単一のカラムのみを取得した場合どうなるでしょうか

```sql
select
    PRICE
from
    transactions m
left outer join
    customer_data d1
on
    m.customer_id = d1.customer_id;
```
結果は以下のとおりです。ディスクの読み込みがありません。全てメモリ内で完結しています。
これは、ウェアハウスキャッシュが効いている証拠です。
メモリ内にデータを保持しているため、わざわざディスクを読み込まずに高速にクエリを返してくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526914/f76c327f-cef4-4c4c-bde8-fd85ae145d11.png)

ウェアハウスが停止すると、このキャッシュは無くなります。

## メタデータキャッシュ
メタデータキャッシュは、Snowflake側が管理しているデータベースに
各種テーブルの統計情報を管理しており、そこからデータを取得する際に働くキャッシュになります。

例えば、よくアンチパターンになる以下のクエリを走らせてみたらどうでしょうか。
```sql
SELECT count(*)
from table
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526914/8815022d-c4c8-47b9-bf48-2139ed331a96.png)

この通り、メタデータキャッシュが効き、ウェアハウスのスキャン量がありません。
最大値や最小値においてもこのメタデータキャッシュが聞くことができ、
有効に使えます。

# 3.参考資料

https://zenn.dev/finatext/articles/snowflake-chache


# 4.tips

実際にクエリを確かめたい場合は、リザルトキャッシュをFALSEに設定しましょう
この際、セッション単位でFALSEにすることをおすすめします。
アカウント単位で実行すると、そのままキャッシュが効かなくなり、もったいないです。

```sql
-- キャッシュを無効化にさせたい場合
ALTER SESSION SET USE_CACHED_RESULT = FALSE;
-- 再度キャッシュを効かせたい場合
ALTER SESSION SET USE_CACHED_RESULT = true;
-- 実際にキャッシュがどうなっているか確認する方法
SHOW PARAMETERS LIKE '%USE_CACHED_RESULT%';

```
cscの作法 その564

# 概要
cscの作法、調べてみた。
grep見つけたので、やってみた。

# 参考にしたページ

https://qiita.com/matarillo/items/15d85999ba6142a33574

# サンプルコード

```
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

public class Searcher {
	protected readonly string[] Words;
	public Searcher(string[] words) {
		Words = words;
	}
	public virtual bool Search(string line) {
		return Words.Any(x => line.Contains(x));
	}
}
public class RegexSearcher: Searcher {
	private readonly Regex pattern;
	public RegexSearcher(string[] words): base(words) {
		Array.Sort(words);
		var p = string.Join("|", words.Select(Regex.Escape));
		pattern = new Regex(p, RegexOptions.Compiled);
	}
	public override bool Search(string line) {
		return pattern.IsMatch(line);
	}
}
public class KmpSearcher: Searcher {
	private readonly Kmp[] matchers;
	public KmpSearcher(string[] words): base(words) {
		matchers = words.Select(x => new Kmp(x)).ToArray();
	}
	public override bool Search(string line) {
		foreach (var m in matchers)
			m.Reset();
		return line.Any(c => matchers.Any(m => m.Match(c)));
	}
	private class Kmp {
		private string pattern;
		private int[] table;
		private int matchingLength = 0;
		public Kmp(string p) {
			pattern = p;
			table = new int[p.Length];
			if (p.Length < 1)
				return;
			table[0] = -1;
			if (p.Length < 2)
				return;
			table[1] = 0;
			int i = 2;
			int length = 0;
			while (i < p.Length)
			{
				if (p[i - 1] == p[length])
				{
					length++;
					table[i] = length;
					i++;
				}
				else if (length > 0)
				{
					length = table[length];
				}
				else
				{
					table[i] = 0;
					i++;
				}
			}
		}
		public bool Match(char c) {
			if (pattern.Length <= matchingLength)
			{
				return true;
			}
			while (pattern[matchingLength] != c)
			{
				matchingLength = table[matchingLength];
				if (matchingLength < 0)
				{
					matchingLength = 0;
					return false;
				}
			}
			matchingLength++;
			return (pattern.Length <= matchingLength);
		}
		public void Reset() {
			matchingLength = 0;
		}
	}
}
public static class GrepUtil {
	public static Func<string[], Searcher> SearcherFactory = words => new Searcher(words);
	public static IEnumerable<String> Grep(TextReader reader, params string[] words) {
		var searcher = SearcherFactory(words);
		string line;
		for (var i = 1; (line = reader.ReadLine()) != null; i++)
			if (searcher.Search(line))
				yield return i + ": " + line;
	}
}
public class Program {
	public static void Main(string[] args) {
		string input = @"Java 1 is nice.
Java 2 is fine.
Java 3 is good.
Java 4 is good.
Java 5 is fine.
Java 6 is good.
Java 7 is good.
Java 8 is marvelous.";
		using (var sr = new StringReader(input))
		{
			List<string> result = GrepUtil.Grep(sr, "fine", "marvelous").ToList();
			foreach (var item in result)
				Console.WriteLine(item);
		}
	}
}





```

# 実行結果

```
>grep0
2: Java 2 is fine.
5: Java 5 is fine.
8: Java 8 is marvelous.

```

以上。
SwitchBotの温湿度計のアドバタイズ

アドバタイズの受信回数をグラフにするとここのところ毎日8時がピークになります。

![filename(16).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/aa19321d-6f3f-428c-83e5-a8f6e41ca63e.png)

疑問だったのですが、温度のグラフを見て分かりました。

![filename(17).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/6d9ac8dd-2b1d-4959-8226-0585fd0954af.png)

温度変化が大きいと送信回数が増えるようです。

この日はノイズの影響が少なかったので、傾向が見えたのだと思われます。日によってずいぶん違います。

5/7から家を空けていて、アドバタイズの受信回数が安定しています。人間の行動によるノイズが不安定にしているようのです。

![filename (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/74ec438b-75e0-4544-adb0-c658ce5d5ab2.png)
音ゲーを作るにあたって、譜面データ作成ソフトを改造してみました

# 音ゲーを作成
以下のyoutubeを見ながら音ゲーを作成しました。

https://www.youtube.com/watch?v=WWeyn4TI0lI&embeds_referring_euri=http%3A%2F%2F192.168.1.147%2F

# 音ゲーの完成
youtubeのものを作成した後は、機能に不足があったので自分で色々と追加してみました。

https://gitlab.com/higashiyama2024fcc/unity_music

# 音ゲーの譜面データ作成
音ゲーを作成するにあたって下記の譜面データ作成ソフトを使用させてもらいました。

https://github.com/setchi/NoteEditor

しかし、使っていると1度作成した音声ファイルの再編集どうすればいいんだろうと思い、jsonファイルを選択するボタンが無かったため、ソースコードを見ながらボタンを増やしてみました。

https://github.com/higayu/NoteEditer_Custom

改造はできたのですが、ソースコードの関数をそのまま利用したら簡単に実装できたので、もしかしたら元から機能がどこかに備わっていたのかもしれません💦

# 内容
変更内容は以下のようになっています。
- jsonファイルの選択ボタンの追加
- 終了ボタンの追加
- 音声ファイルの選択パスのデフォルトをWindowsのダウンロードフォルダに変更
- 保存したjsonファイルはWindowsのダウンロードフォルダの直下に「音声ファイル名.json」で保存されるように変更しました。

# 注意点
前回編集したファイルを続きから編集したい場合は、音声ファイルを選択してから、jsonファイルを選択してください。
![使い方.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957418/87603452-2cd5-41b1-ac1d-cbf638511725.png)
【Windows】7zipで分割した大容量ファイルをコマンドプロンプトで結合する方法

# ◆概要
centos(約12GB、isoファイル)を7zipを使って分割、その後、Windowsのコマンドプロンプトを使用して分割したファイルを結合します。
大容量ファイルを持ち込む場合に有効です。
現場によっては、１０GB以上のファイルを一気に持ち込めなかったりするので。
また、コマンドプロンプトで結合するので汎用性が高いです。(Windowsにコマンドプロンプトは標準で搭載されているので。)
# ◆前提
7zipがインストールされていること。
# ◆ハッシュ値の確認
まずは、ファイルを分割する前に、分割するファイルのハッシュ値を取得します。結合後のファイルで取得したハッシュ値と突き合わせるためです。
### ▼手順
1．Windowsでコマンドプロンプトを立ち上げる
2．コマンドプロントンに以下のコマンドを入力する
```
certutil -hashfile [ファイル名] SHA256
```
★今回はSHA256でハッシュ値を取得する
★[ファイル名]は、ハッシュ値を取るファイルをドラッグアンドドロップでコマンドプロントに入れると、自動で入力される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761139/eb05e9f4-5dae-415e-96b9-0b6b9c9a5260.png)
# ◆7zipでファイルの分割
7zipの機能を利用して、isoファイルを分割します。
### ▼手順
1．分割対象のファイルを右クリックし、[7zip]-[圧縮]をクリックする
2．[アーカイブ形式]に[zip]を選択する
※今回はzip形式で圧縮するため
3．[分割ボリュームサイズ]に[1000M]を選択する
4．[OK]をクリックする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761139/2f9da9d1-694d-4c48-ad30-fd2af02bfa8d.png)
5．指定したフォルダに、分割されたファイルが存在することを確認する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761139/bc3e530f-37f4-4cbd-8b46-8398d8291121.png)
# ◆コマンドプロンプトでファイルの結合
分割したファイルを、コマンドプロントを用いて結合していきます。
### ▼手順
1．Windowsの検索欄に[cmd]を入力し、コマンドプロントを立ち上げる
2．分割ファイルのあるフォルダにcdコマンドで移動する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761139/af5a8e57-68f6-414e-835d-87494c41ee83.png)
3．コマンドプロントに以下のコマンドを流す
```
COPY /b CentOS-Stream-9-latest-x86_64-dvd1.zip.001 + CentOS-Stream-9-latest-x86_64-dvd1.zip.002 + CentOS-Stream-9-latest-x86_64-dvd1.zip.003 + CentOS-Stream-9-latest-x86_64-dvd1.zip.004 + CentOS-Stream-9-latest-x86_64-dvd1.zip.005 + CentOS-Stream-9-latest-x86_64-dvd1.zip.006 + CentOS-Stream-9-latest-x86_64-dvd1.zip.007 + CentOS-Stream-9-latest-x86_64-dvd1.zip.008 + CentOS-Stream-9-latest-x86_64-dvd1.zip.009 + CentOS-Stream-9-latest-x86_64-dvd1.zip.010 + CentOS-Stream-9-latest-x86_64-dvd1.zip.011 + CentOS-Stream-9-latest-x86_64-dvd1.zip.012 CentOS-Stream-9-latest-x86_64-dvd2.zip		
```

<font color="blue">★注意
出力するファイルは拡張子付きで指定すること
今回の例だと、[CentOS-Stream-9-latest-x86_64-dvd2.zip]
出力するファイルをCentOS-Stream-9-latest-x86_64-dvd2(拡張子ヌケ)で指定すると、zipファイルが作成されない	
</font>
4．分割ファイルのあるフォルダに、結合されたzipファイルがあることを確認する
※今回だと、CentOS-Stream-9-latest-x86_64-dvd2.zip
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761139/c62cbdf0-4e13-401e-87ec-45f331bcda45.png)
5．あとは、このzipファイルを展開する
※展開前に、元のファイルは退避させておく(新しいファイルに上書きされるため。)

# ◆ファイル結合後のハッシュ値確認
『◆ハッシュ値の確認』手順を参考にハッシュ値を取得します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761139/7549cdf0-cefd-4ab5-a61b-b7eaf08dca40.png)

# ◆参考
https://codaholic.org/?p=1770

https://qiita.com/magiclib/items/a3044c21d195e542472f
Go1.24GenericsのType Aliasを利用する

## はじめに
Go 1.24のリリースに伴い、Generics type aliasが利用できるようになりました。
実際に触ってみたく、記事にしました！

## スライスのジェネリック型エイリアス

```go:
package main

import "fmt"

// スライス型のエイリアス
type List[T any] = []T

func main() {
	// int型のList
	var intList List[int] = []int{1, 2, 3}
	fmt.Println(intList) // 出力: [1 2 3]

	// string型のList
	var stringList List[string] = []string{"A", "B", "C"}
	fmt.Println(stringList) // 出力: [A B C]
}

```
:::note info
`type List[T any] = []T`はT はジェネリック型パラメータで、「任意の型（any）」を受け取ることを意味します。
:::

```go:実行結果
~/go/go_generics  $ go run main.go 
[1 2 3]
[A B C]
```

```go:
// Map型のエイリアス
type Dictionary[K comparable, V any] = map[K]V

func main() {
	// string → int のマップ
	var ageMap Dictionary[string, int] = map[string]int{
		"Alice": 30,
		"Bob":   25,
	}
	fmt.Println(ageMap) // 出力: map[Alice:30 Bob:25]

	// int → string のマップ
	var idMap Dictionary[int, string] = map[int]string{
		1: "Alice",
		2: "Bob",
	}
	fmt.Println(idMap) // 出力: map[1:Alice 2:Bob]
}
```
:::note info
`type Dictionary[K comparable, V any] = map[K]V`
Kはマップのキーとなる型で、==や!=で比較できる型に限定されます。
:::

```go:
~/go/go_generics  $ go run main.go
map[Alice:30 Bob:25]
map[1:Alice 2:Bob]
```

```go:
package main

import "fmt"

// ジェネリック構造体
type Container[T any] struct {
	Value T
}

// 型エイリアス
type Box[T any] = Container[T]

func main() {
	// BoxはContainerと同じ役割
	intBox := Box[int]{Value: 100}
	fmt.Println(intBox)  // 出力: {100}

	stringBox := Box[string]{Value: "Hello"}
	fmt.Println(stringBox)  // 出力: {Hello}
}
```

```go:実行結果
~/go/go_generics  $ go run main.go
{100}
{Hello}
```

## 型に制約を持たせる

```go:main.go
package main

import "fmt"

// 制約: 数値型のみ
type Number interface {
	int | float64
}

// 制約付きジェネリック構造体
type Container[T Number] struct {
	Value T
}

func main() {
	// int型は許可
	intBox := Container[int]{Value: 100}
	fmt.Println(intBox) // 出力: {100}

	// float64型も許可
	floatBox := Container[float64]{Value: 99.9}
	fmt.Println(floatBox) // 出力: {99.9}

	// string型はエラー
	stringBox := Container[string]{Value: "Hello"} // コンパイルエラー
}
```
→確かにエラーが起きていることを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/98a692c2-d39a-4355-ac0b-430ff764062c.png)


## 型の拡張
上記例の場合、Number型しか許容していませんでした。ただ、Stringも対応させたい場合は下記のように実施します。

```go:main
package main

import "fmt"

// 数値型制約
type NumberInterface interface {
	int | float64
}

// 文字列型制約
type StringInterface interface {
	string
}

// NumberまたはStringを許可
type NumberOrString interface {
	NumberInterface | StringInterface
}

// 制約付きジェネリック構造体
type Container[T NumberOrString] struct {
	Value T
}

func main() {
	// 許可された型
	intBox := Container[int]{Value: 100}
	fmt.Println(intBox)  // 出力: {100}

	floatBox := Container[float64]{Value: 99.9}
	fmt.Println(floatBox)  // 出力: {99.9}

	stringBox := Container[string]{Value: "Hello"}
	fmt.Println(stringBox)  // 出力: {Hello}

	// 許可されない型（例: bool）
	// boolBox := Container[bool]{Value: true}  // コンパイルエラー
}
```
→確かにStringは許容し、他はエラーが起きていることを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/e9a1fc46-33a8-44a5-8fc1-2e784b7be0bd.png)

## 最後に
Genericsを使うことで今まで汎用化できなかった箇所も改善されそうですね！
LightWeightIP(LwIP)を紐解く(概要・構造)


仕事や勉強用でLightWeightIPを使うことが多くなってきたので、振る舞いについて記事にまとめていこうと思っています。
本記事はLightWeightIPの概要と構造にフォーカスして説明します。

# LightWeightIPとは

LightWeightIPは、オープンソースのTCPIPのプロトコルスタックです。省メモリを特徴としており、組み込み開発でよく使用されます。
FreeRTOSにも対応しており、AWSとの連携に使用されることもあります。
LightWeightIPのGithubリポジトリは[こちら](https://github.com/lwip-tcpip/lwip)です。


# LightWeightIPの特徴

LightWeightIPは、下記の図のようにアプリケーションからAPIを呼び出すことで動作します。ここは他のライブラリと変わりはないと思います。

![LwIPブロック図.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266944/67bcbe9a-cfd6-6e19-0f45-5b789e6255d5.png)

ただ、上記の図からもわかる通り、LightWeightIPにはEthernetやWLANのデバイスドライバは付属しておらず、自作するか、マイコンメーカが公開しているデバイスドライバを使用する必要があります。

# LightWeightIPの構造

ここでLightWeightIPの構造を下記の図にに示します。

![LwIPブロック詳細図.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266944/8f8eb80c-cd36-2fa3-7002-58e86bb9ab2d.png)

上記の通り、LightWeightIPにはアプリケーション層のAPI、つまりHTTPの通信やソケット通信を行うAPIが用意されています。上記の図で言うと「apps」と「api」がこれに当たります。
そして、「apps」と「api」はその下の「core」のモジュールを使ってTCP/IPの通信を行います。
「core」層は、「netif」層を介してデバイスドライバを使用し、Ethernet, WLANの通信を行います。「netif」層は、デバイスドライバをLightWeightIPにとって都合の良い形で呼び出すためのモジュールとなります。

# LightWeightIPのフォルダ構成

[LightWeightIPのGithubリポジトリ](https://github.com/lwip-tcpip/lwip)のフォルダ構成からも見てもわかる通り、フォルダ構成は下記のようになっています。ここではソースコードが入っているフォルダ「src」以外の説明は割愛します。

![LwIPフォルダ構成.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266944/bd4ab460-8eda-4872-b317-1bec64c39f90.png)

上記のとおり、前章で記述した、モジュールがそのままフォルダでまとめられています。「apps」フォルダや「api」フォルダには、アプリケーション層の通信を行うためのAPIが定義されています。
「core」フォルダには、TCPIPの制御・通信用の関数が定義されています。
そして、「netif」フォルダには、デバイスドライバの制御を行うための関数が定義されています。

# 参考資料

- Githubリポジトリ
https://github.com/lwip-tcpip/lwip

- LightWeightIP公式サイト
https://savannah.nongnu.org/projects/lwip/
Data Cloudでデータを取得するのには時間が掛かる？

+ [Data Cloudのまとめに戻る](https://qiita.com/Keiji_otsubo/items/ce366c02186b6bb5b39b)

------
+ [DataStreamでエラーになった場合のデバック方法](https://qiita.com/Keiji_otsubo/items/46b31c605f83f25d92f4)


# Get Started with Unstructured Data in Data Cloud

+ [Anyone else getting "We've hit a Snag" after choosing "Knowledge Article Version" within playground?](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX000000TzM4)

:::note
何が起こったのかはわかりませんが、今朝はうまくいきました。おそらく、データが読み込まれるまで十分に待たなかったのだと思います。 
:::

+ [NEED YOUR HELP! The status in the Last Run Status hasn't been updated after 15 minutes of waiting](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX000000TVOm0AO)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/0dfc6bbf-653c-40c8-b53a-bf3b1adc2553.png)

:::note
Just to update - After creating a new playground, time solves the issue. 
:::



# We've hit a Snagは時間が掛かるだけでない？

+ [Redshift を統合し、データ ストリームを設定しました。その後、ユーザー プロファイル オブジェクトを個人および連絡先メールにマッピングしました。ただし、SFMC をアクティベーション ターゲットとして「個人」にセグメントを作成しようとすると、セグメントの公開時にエラーが発生しました。](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007er4NU)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/ca0d61b9-074d-4a09-7b0d-10c734d145fa.png)

:::note
発生しているエラー メッセージは、アクティベーション エンジンがデータ列との競合を検出したときに、Marketing Cloud アクティベーションでよく表示されます。

この場合に最も多く発生する問題は、アクティブ化されたデータを受信するデータ拡張機能をクライアントが誤って変更し、DE 列に何か変更が生じた場合です。

MC にデータを送信したことがない新しいアクティベーションの場合、複数の DMO 間でフィールド名が競合すると、この問題が発生する可能性があります。通常、名前は DMO 間で共通である可能性があるため、何らかの ID を含むフィールドがこれに該当します。アクティベーションで選択している属性に ID フィールドが含まれている場合は、そのフィールドに優先属性名を使用して、ID に含まれる内容や ID の取得元が明確になるようにしてください。これにより、多くの場合、フィールドの競合が解決され、アクティベーションを続行できるようになります。

参照: https://help.salesforce.com/s/articleView?id=sf.c360_a_activation_troubleshooting.htm&type=5 
:::

+ [Troubleshooting BYO-LLM](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007cmLFn)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/4d38e445-5cdb-4b88-c324-7dbb265da97e.png)
【AWS Cafeteria】社外勉強会 参加レポート

## イベントへの参加経緯
AWSについて、資格やハンズオンで学習することが多かったので、もっと生の経験から学びたい！というモチベーションがありました。
また、今までの2年間の自分のAWS経験をアウトプットする機会が欲しかったこともあり、compassを彷徨っていたらぴったりそうなアジェンダのイベントがあり、申し込みしました。

また、最近友人同士で勉強会を開いており、もっと参加者を増やしたり会の雰囲気を良くするにはどんなことが必要かも知りたくて参加しました。

自分が参加したパートで話していたことを補足しながらまとめてみます！

イベントリンク:
https://yumemi.connpass.com/event/343066/
アジェンダ一覧：
①失敗から学ぶ！本当にあったしくじり話

②マルチアカウント管理のベストプラクティス

③こんなサービス欲しい！AWSにあったら嬉しいサービス

④あなたの会社でやっているコスト最適化術

## 参加したアジェンダ　失敗から学ぶ！本当にあったしくじり話

#### 前提
まず、Lambdaとは？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/4690c321-1b3f-4722-89ca-2dfe60d8bd7e.png)


サーバレスなコンピューティングサービスで、インフラの管理をAWSに委託することができ、自動でスケールし稼働した分(リクエスト回数と処理の実行時間)だけ課金が発生するサービスです。

APIGatewayやS3,SQSなどのイベントソースと組み合わせてイベントに反応して関数を実行することができるサービスです。

このように便利なサービスではありますが、同時実行数や処理可能時間、メモリ量なとに制限があります。


そのため、大きなメモリが必要であったり、処理に多くの時間がかかる動作を行いたいときは別のコンピューティングサービスを使用する必要があります。


下記画像はニーズに応じてどのコンピューティングサービスを使用するべきか簡単にまとめられているものです。
![Screenshot_20250221-210605.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/414d2bfd-4f84-44d9-9a34-6d0e045f5096.png)

ちなみに、このサービスは自分もとてもお世話になっていますが、同時実行数の制限に将来怯えることになるなあと思っています。


次にSQSです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/80aee8f7-15f6-4e54-a129-70f10f8de2cf.png)


マイクロ化したアプリケーション同士を柔軟性高く非同期に繋げるためのサービスです。

イメージで説明すると、下図のようにSQSがない状態だとせっかくアプリケーションを細かく分割しても、そのアプリケーション同士が密結合になっており、柔軟性が低い状態です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/2bfe982c-e439-4784-ad8c-3531e2f099b9.png)

そこで、このアプリケーション間にSQSを挟みます。すると、アプリケーション同士が疎結合化し、非同期処理を実現したりリトライなどのエラーハンドリングを容易にすることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/1d8b93b0-8c81-4d9f-bddb-dbfb7928b29c.png)

### しくじり談：SQS/Lambdaの無限ループでコスト大
上記のようにSQS-Lamdba構成は一般的なアーキテクチャではあるのですが、
機能の設定を誤って大変なことを起こしてしまった事例があったようです。

具体的には、Lambda(ここでいうapp2)の処理時間が長期化しており、SQSで設定する「可視性タイムアウトリトライ」(設定した秒数が経過しないと、リトライを行わないようにする設定)を超えてしまっていたようです。

そのような状況で、Lambdaでエラーが発生してSQSにメッセージが残り続けていると大量の回数SQSのメッセージをトリガにLambdaが起動してしまい、ランニングコストが跳ね上がってしまう、という事態につながったということでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579705/21af6fb6-5c96-4e5c-9e41-9de81b185203.png)

### イベントの感想
今回紹介した話題だけでなく様々な失敗エピソードを共有できてとても楽しかったし学びになりました。

かっこいい成功エピソードはネット上にもたくさんありますが、このような失敗エピソードはオフラインだからこそしゃべれる内容だし、自分も同じ失敗をしないようにAWSリソースを確認する観点を養うことができたと感じました。

今後もこのようなイベントに積極的に参加したり、機会があればイベントの運営側もやってみたいと思いました。

イベントを開催していただいた三社様、イベント運営いただいた方々ありがとうございました！
Bihari型不等式の証明

# 前置き
例えばYamada Watanabeの定理を示すとき、二つの解の差が0であることを示すときに、Gronwallの拡張を使いたいことがある。これはその拡張の定理の証明である。
しばしばOsgood Criterion (Osgoodの条件)といわれることもあるっぽい？(あんまり詳しくないので間違ってたらすみません)
一応、言っておくとこれはBihari型不等式自体の証明ではなく、そこにOsgood条件(関数を分母にとったものの積分が無限)のときに、不等式の左辺が必ずすべての$t$について0になることを示した。


# 定理
$u,\beta,\omega:\mathbb{R}\_+ \rightarrow \mathbb{R}\_+$を3つの連続関数とし, $\omega$は正値増加関数とする。$u$が次の不等式を$\displaystyle \int_0^t \beta(s)\,ds<\infty$の下で満たすとする。
```math
u(t)\leq \int_0^t\beta (s)\omega\left(u(s)\right)ds \quad t\in\mathbb{R}_+
```
ここで
```math
\int_0^x\frac{dy}{\omega(y)}=\infty\quad \text{for all } x>0
```
このとき $u(t)=0$がすべての$t \in \mathbb{R}_+$について成り立つ。

# 証明
まずいかを定義する。
```math
v_{\varepsilon}\left(t\right) := \varepsilon+\int_0^t\beta (s)\omega\left(u(s)\right)ds,\quad \varepsilon>0
```
ならば(自明であるが)$u(t)\leq v_{\varepsilon}(t)$.また次も定義する。
```math
F(x) := \int_{1}^x\frac{dy}{\omega(y)} \quad  x>0
```
ここでこの積分はリーマン積分とする。ならば次が計算できる。
```math
\frac{d}{dt}F\left(v_{\varepsilon}(t)\right) = F'\left(v_{\varepsilon}(t)\right)v_{\varepsilon}'(t) = \frac{1}{\omega \left(v_{\varepsilon}(t)\right)}\beta(t)\omega(u(t)) \leq \beta(t)
```
$0$から$t$の範囲で積分すると
```math
F\left(v_{\varepsilon}(t)\right)-F\left(v_{\varepsilon}(0)\right)\leq \int_{0}^t \beta(s)\,ds
```
次の極限を考える。$\varepsilon \rightarrow 0$. このとき、
```math
\lim_{\varepsilon\to0}\int_{1}^{v_{\varepsilon}(0)}\frac{dy}{\omega(y)}=\int_1^{0}\frac{dy}{\omega(y)} = -\infty
```
すると不等式は
```math
\begin{align*}
    F\left(v_{\varepsilon}(t)\right)-F\left(\varepsilon\right)\leq \int_{0}^t \beta(s)\,ds\Leftrightarrow  F\left(v_{\varepsilon}(t)\right)\leq \int_{0}^t \beta(s)\,ds +F\left(\varepsilon\right) \\
    \Rightarrow \limsup_{\varepsilon \to 0}F\left(v_{\varepsilon}(t)\right) \leq \int_{0}^t \beta(s)\,ds +\lim_{\varepsilon \to 0}F\left(\varepsilon\right) = -\infty
\end{align*}
```
$F$は狭義増加関数より,$\varepsilon\to0$による条件$F\left(v_{\varepsilon}(t)\right) \to -\infty$ は次を表す。$v_{\varepsilon}(t) \to 0$. よって
```math
u(t)\leq \lim_{\varepsilon\to 0}v_{\varepsilon}(t)=0
```
がすべての$t$について成り立つ。
ElastiCache for Redis と Memcached の比較とユースケース


ElastiCache for Redis と Memcached は、AWS が提供するインメモリデータストアで、それぞれ異なるユースケースに適しています。

---

## **ElastiCache for Redis のユースケース**
Redis は **永続化**、**Pub/Sub**、**高性能キャッシュ** などの機能を持つため、以下のような用途で利用されます。

1. **アプリケーションのキャッシュ**
   - データベースの負荷を軽減し、応答速度を向上させるために使用。
   - 例: Rails や Node.js などの Web アプリケーションのセッションデータ、ページキャッシュ。

2. **ランキング & リーダーボード**
   - Redis の Sorted Set を利用し、リアルタイムでスコアランキングを管理。
   - 例: ゲームアプリのスコアランキング。

3. **Pub/Sub を使ったリアルタイムメッセージング**
   - Redis の Pub/Sub 機能を利用して、リアルタイム通知やチャットシステムを構築。
   - 例: SNS やゲームのイベント通知。

4. **分散ロック**
   - `SETNX` (SET if Not Exists) を使って分散ロックを実装。
   - 例: 複数サーバー間で同じリソースの同時更新を防ぐ。

5. **キュー (Queue) システム**
   - `LPUSH` & `RPOP` を使ってキューを実装し、非同期処理を管理。
   - 例: バックグラウンドジョブ (Sidekiq, Resque など)。

6. **セッションストア**
   - ショッピングサイトなどで、ログイン情報を Redis に保存。
   - 例: Ruby on Rails + Devise のセッションストア。

### **Redis の構成図**

![aws_elasticache_redis.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2294049/fb919575-f73b-4800-b248-b1aeb2f94531.png)

---

## **ElastiCache for Memcached のユースケース**
Memcached は **単純なキャッシュ** に特化しており、スケールしやすいのが特徴。

1. **データベースクエリの結果キャッシュ**
   - 高頻度でアクセスされるデータをキャッシュし、データベース負荷を軽減。
   - 例: SQL の `SELECT` 結果を Memcached に保存。

2. **セッションストア**
   - アプリケーションのセッションデータを保存し、ステートレスな構成を実現。
   - 例: PHP のセッション管理 (Redis でも可能だが、単純な用途なら Memcached でも OK)。

3. **API レスポンスのキャッシュ**
   - 外部 API の結果をキャッシュし、リクエスト負荷を減らす。
   - 例: 天気情報、ニュースフィードのキャッシュ。

4. **オブジェクトキャッシュ**
   - アプリケーションの計算コストが高いデータをキャッシュ。
   - 例: 計算済みのランキングデータ、JSON オブジェクト。

### **Memcached の構成図**

![aws_elasticache_memcached.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2294049/f1df8b66-c81e-4934-b612-d714c264b28c.png)

---

## **Redis vs Memcached の選び方**
| 比較項目          | Redis | Memcached |
|----------------|--------|------------|
| データ構造 | リスト, ハッシュ, セット, ソートセット など | シンプルな key-value |
| ストレージ | ディスク永続化 (RDB, AOF) | 永続化なし (揮発性) |
| スケーリング | クラスターによるスケールアウト | 水平方向にスケール可能 |
| マルチスレッド | シングルスレッド (ただし高性能) | マルチスレッド |
| TTL 設定 | 可能 | 可能 |
| ユースケース | 高度なデータ処理、Pub/Sub、ランキング、ジョブキュー | 単純なキャッシュ用途 |

---

## **どのような構成で利用するか？**
### **1. Webアプリのキャッシュ**

![web_app_cache.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2294049/fb4c0ff7-c20c-4ad8-a398-cb1b2d8fdcfe.png)

- **構成:** 
  - ElastiCache for Redis (シングルノード or マスター/スレーブ)
  - Rails, Django, Node.js などのフレームワーク + Redis
- **用途:** 
  - セッション管理 (`redis-session-store`)
  - ページキャッシュ
  - API レスポンスのキャッシュ

### **2. 高トラフィックな API のレスポンスキャッシュ**

![api_response_cache.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2294049/27990653-3f90-4fce-96a1-fca04e35ee22.png)

- **構成:**
  - ElastiCache for Memcached (複数ノードで分散)
  - API サーバー (Lambda, EC2, Fargate)
- **用途:**
  - REST API / GraphQL のレスポンスキャッシュ
  - DB クエリキャッシュ

### **3. リアルタイムデータ (チャット, ランキング, ジョブキュー)**

![real_time_data.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2294049/379862b6-0595-467f-a266-7b1855aa8102.png)

- **構成:**
  - ElastiCache for Redis (クラスターモード)
  - Pub/Sub, Sorted Set, List 機能を利用
- **用途:**
  - ランキングの管理
  - メッセージのリアルタイム通知
  - Sidekiq のジョブキュー管理
paizaラーニングレベルアップ問題集の【半角スペース区切りでの文字列の分割】をやってみた。

paizaラーニングレベルアップ問題集の【半角スペース区切りでの文字列の分割】をやってみました。
- - -
##### 問題
###### 2つの文字列の半角スペース区切りでの分割
https://paiza.jp/works/mondai/stdin_primer/stdin_primer__split_string_step1
###### 3つの文字列の半角スペース区切りでの分割
https://paiza.jp/works/mondai/stdin_primer/stdin_primer__split_string_step2
###### 5つの文字列の半角スペース区切りでの分割
https://paiza.jp/works/mondai/stdin_primer/stdin_primer__split_string_boss
- - -
以下のコードの`one two three four five`の部分を
- `Hello paiza`にすれば1問目の解答
- `He likes paiza`にすれば2問目の解答

になります。
- - -
##### C
```c
#include <stdio.h>

const char *S = "one two three four five";

int main(void){
	const char *p = S;
	char s[6];
	while (sscanf(p, "%s", s) == 1) {
		p += printf("%s", s);
		puts("");
		for (; *p && *p == ' '; p++);
	}
	return 0;
}
```
- - -
##### C++
```cpp
#include <iostream>
#include <sstream>
using namespace std;

const string S = "one two three four five";

int main(void){
	stringstream ss(S);
	string s;
	while (ss >> s) cout << s << endl;
	return 0;
}
```
- - -
##### C#
```cs
using System;

class Program
{
	const string S = "one two three four five";
	
	public static void Main()
	{
		foreach (string s in S.Split()) Console.WriteLine(s);
	}
}
```
- - -
##### Go
```go
package main
import "fmt"

const S = "one two three four five"

func main(){
	var s string
	p := 0
	for i, err := fmt.Sscanf(S, "%s", &s); err == nil && i == 1; i, err = fmt.Sscanf(S[p:], "%s", &s) {
		k, _ := fmt.Print(s)
		fmt.Println()
		p += k
		for p < len(S) && S[p] == ' ' {p++}
	}
}
```
- - -
##### Java
```java
public class Main {
	
	static final String S = "one two three four five";
	
	public static void main(String[] args) {
		for (String s : S.split(" "))
			System.out.println(s);
	}
}
```
- - -
##### JavaScript
```js
const S = "one two three four five";
S.split(' ').forEach((s) => console.log(s));
```
- - -
##### Kotlin
```kotlin
val S = "one two three four five"

fun main() {
	for (s in S.split(' ')) println(s)
}
```
- - -
##### PHP
```php
<?php
	const S = "one two three four five";
	foreach (explode(' ', S) as $s) echo $s, PHP_EOL;
?>
```
- - -
##### Perl
```pl
use constant S => "one two three four five";
foreach $s (split ' ', S) {
	print "$s$/";
}
```
- - -
##### Python3
```py
S = "one two three four five"
for s in S.split():
	print(s)
```
- - -
##### Ruby
```rb
S = "one two three four five"
S.split.each do |s|
	puts s
end
```
- - -
##### Scala
```scala
object Main extends App{
	val S = "one two three four five"
	for (s <- S.split(' ')) {
		println(s)
	}
}
```
- - -
##### Swift
```swift
let S = "one two three four five"
for s in S.split(separator: " ") {
	print(s)
}
```
[Memory] 大容量記憶装置（Mass Storage Device）について

# 大容量記憶装置（Mass Storage Device）の比較

| 項目 | **HDD**（Hard Disk Drive） | **USBフラッシュドライブ** | **SSD**（Solid State Drive） |
|:--|:--|:--|:--|
| **記録方式** | 磁気ディスク | フラッシュメモリ | フラッシュメモリ |
| **特徴** | - 大容量でコストパフォーマンスが高い<br>- 機械的な部品（プラッタ、ヘッド）を使用<br>- 衝撃に弱い | - 小型・軽量で持ち運びに便利<br>- 書き換え回数やデータ保持期間に制限あり | - 高速アクセス（ランダムアクセス性能が高い）<br>- 省電力・静音・耐衝撃性に優れる<br>- 書き換え回数やデータ保持期間に制限あり |
| **コストパフォーマンス** | 高い | 低い | 中程度 |
| **耐衝撃性** | 低い | 高い | 高い |
| **長期保存適性** | 不向き(耐衝撃性) | 不向き(書き換え回数やデータ保持期間) | 不向き(書き換え回数やデータ保持期間) |
| **速度** | 遅い（SSDより） | 速い（HDDより） | 速い（HDDより）<br>**NVMe接続でさらに高速** |

# 参考リンク
- [HDDの仕組み](https://ja.wikipedia.org/wiki/ハードディスクドライブ)
- [USBフラッシュドライブ](https://ja.wikipedia.org/wiki/USBフラッシュドライブ)
- [SSDとは](https://ja.wikipedia.org/wiki/ソリッドステートドライブ)

# Ping-t
- [3381](https://mondai.ping-t.com/question_subjects/31/question_sessions/30862848/progressing_questions/472796580)
ろうとるがPythonを扱う、、（その29：CSVファイルの列の入れ替え）

# 列の入れ替え
今回も小ネタ。個人的に少々苦労したので記録。CSVファイルの列の入れ替えである。エクセルやpandasライブラリ簡単にできそうなのであるが、辞書を使って、自身のコードで明示的に入れ替えたいなどの理由から実施。

## ソースコード
### サンプルCSV
実際には30列以上あるものを対象としていたが、ここでは簡単なサンプルとして下記のようなものを対象とする。まず、データは下記の順番で記載されている、、、という前提がある。

```
id,family,name,age,nationality
```

実際のCSVファイルはこちら。

```sample.csv
2,Tanaka,Koh,45,Japan
4,Jon,Andersson,64,Sweden
7,Emily,Wang,34,Taiwan
```



下記がソースコード。コメントも含めている。

```python:column.csv
# -*- coding: utf-8 -*-
import csv

# 列番号用辞書：数値が列番号、この数値を書き換えることにより、列の入れ替えを実施
dic = {'id':2, 'family':3, 'name':5, 'age':4, 'nationality':1}
# データ一時格納用辞書
data = {'id':'', 'family':'', 'name':'', 'age':'', 'nationality':''}

# 列番号でソート
sorted = sorted(dic.items(), key=lambda x:x[1]) # tuple
print(sorted)
# タプルを辞書化
dic_sorted = {k: v for k, v in sorted} # to dictionaly
print(dic_sorted)
# ソートされた列のヘッダを取得
list_sorted = [k for k, v in sorted]
print(list_sorted)

# 対象ファイル読み込み
r_file = open('sample.csv', 'r')
r_data = csv.reader(r_file)
# 書き込みファイル
w_file = open('output.csv', 'w', newline='')
writer = csv.writer(w_file)
# ヘッダ書き込み
writer.writerow(list_sorted)
# 読み込みデータを１行ずつ処理
for row in r_data:
    # データを辞書に登録
    data['id'] = row[0]
    data['family'] = row[1]
    data['name'] = row[2]
    data['age'] = row[3]
    data['nationality'] = row[4]
    # 辞書をリスト化
    write_list = [data[i] for i in list_sorted]
    #print(write_list)
    # データ書き込み
    writer.writerow(write_list)
# ファイルクローズ
r_file.close()
w_file.close()
```

## テスト
実行。

```shell
$ python3 column.py
[('nationality', 1), ('id', 2), ('family', 3), ('age', 4), ('name', 5)]
{'nationality': 1, 'id': 2, 'family': 3, 'age': 4, 'name': 5}
['nationality', 'id', 'family', 'age', 'name']
```

結果ファイルは下記となる。

```output.csv
nationality,id,family,age,name
Japan,2,Tanaka,45,Koh
Sweden,4,Jon,64,Andersson
Taiwan,7,Emily,34,Wang
```

次に、ソースコードを下記のように書き換える。

```python
dic = {'id':1, 'family':3, 'name':2, 'age':5, 'nationality':4}
```

この時の結果ファイルは下記となる。

```output.csv
id,name,family,nationality,age
2,Koh,Tanaka,Japan,45
4,Andersson,Jon,Sweden,64
7,Wang,Emily,Taiwan,34
```

## 参考URL
- [Pythonの辞書型ソートしてみた！](https://zenn.dev/park_udp/articles/pythonmemoone)
【OCI】importブロックでオブジェクトストレージをインポートする【リソースマネージャ】

# importブロックの基本構文

v1.5からimportブロックを利用し、Terraformの管理下にない既存のリソースをインポートできるようになりました。(OCIでの)基本的な書き方は以下の通りです。

```hcl:main.tf
import {
    to = module.storage.oci_objectstorage_bucket.this["log"]
    id = "<OCID>"
}

module "objectstorage" {
    source = "../../modules/objectstorage"
# 以下略
```

しかし、オブジェクトストレージをインポートする際にこのように書いても、以下のエラーになります。

```hcl
Error: can not marshal to path in request for field NamespaceName. Due to can not marshal a nil pointer
```

# Terraformのドキュメントを確認

https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/objectstorage_bucket

各ページの最下部にインポート用のコマンドサンプルが記載されており、それによると、ネームスペースとバケット名の記載が必要なようです。

![スクリーンショット 2025-02-22 9.11.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/136326/80ce5f53-9b0f-425d-be14-dcae2fbf467e.png)

# オブジェクトストレージのimportブロック

というわけで、オブジェクトストレージの場合は以下のように書くことでインポートできます。

```hcl:main.tf
import {
    to = module.storage.oci_objectstorage_bucket.this["log"]
    id = "n/<name_space>/b/<bucket_name>"
}
```
PHPで「変数の宣言と値の代入」の動作を確認してみた。

# 概要

PHPで「変数の宣言と値の代入」の動作を確認してみました。
以下のページを参考にしました。

https://www.javadrive.jp/php/var/index1.html

# 実装

以下のファイルを作成しました。

```sample.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>PHPテスト</title>
</head>
<body>

<p>PHPのテストです。</p>

<p>
<?php
$name = '加藤';
print $name.'<br />';

$num = 20;
print $num * 9;
?>
</p>

</body>
</html>
```

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

```
$ php sample.php 
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>PHPテスト</title>
</head>
<body>

<p>PHPのテストです。</p>

<p>
加藤<br />180</p>

</body>
</html>
```

# まとめ

何かの役に立てばと。
Data Security>Control Access to Fields module 設定例


# 設定例

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/166bad42-5ddd-be3c-429d-f9d8ab0534b3.png)


+ Enable Job Application Object Permissions: Read and Edit
+ Enable Job Application Field Permissions: Assigned users can view all fields and edit all fields

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/c2117a07-de25-c67e-29ed-6ac7d2543fc2.png)

+ except they can’t edit Position and Candidate

この設定は関係なかった...

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/7a4377cb-a9f5-46a6-89c3-7fb094c7cbb3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/fcb35a08-5a55-b500-f6a0-97ea468e149a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/a2981b94-eeb9-9f1e-1eae-e85c8ff8e21a.png)

# よく分からない

+ [We can't find that the ‘Manage Job Applications’ permission set grants read access and edit access to all Job Application fields, except edit access for Position and Candidate. ](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000MbFj0)