Skip to content

Commit e731186

Browse files
committed
docs: イベントデータ分離設計(UpcomingEvents vs EventHistory)の文書を追加
- 2つのテーブルの役割と分離設計を説明 - データフローとライフサイクルを図解 - キャンセルイベントが統計に含まれない理由を明記 - 実際の2025年1月Doorkeeper問題を事例として記載 - トラブルシューティングガイドを含む 既存の命名規則(plan_*)に従ってファイル名を設定
1 parent e4cbb71 commit e731186

File tree

1 file changed

+231
-0
lines changed

1 file changed

+231
-0
lines changed

docs/plan_event_data_separation.md

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
# イベントデータアーキテクチャ設計
2+
3+
## 概要
4+
5+
CoderDojo.jp では、イベントデータを **UpcomingEvents(未来のイベント)****EventHistory(過去のイベント)** の2つのテーブルに分離して管理しています。この設計により、パフォーマンスの最適化と統計の正確性を両立しています。
6+
7+
## 🏗️ アーキテクチャ
8+
9+
### データベース構造
10+
11+
```
12+
upcoming_events テーブル event_histories テーブル
13+
(未来) (過去)
14+
│ │
15+
│ 時間の経過により移行 │
16+
└────────────→─────────────┘
17+
```
18+
19+
### 2つのテーブルの役割
20+
21+
| 項目 | UpcomingEvents | EventHistory |
22+
|------|---------------|--------------|
23+
| **テーブル名** | `upcoming_events` | `event_histories` |
24+
| **対象期間** | 未来のイベント | 過去のイベント |
25+
| **主な用途** | Webサイトの「近日開催」表示 | 統計グラフ・履歴分析 |
26+
| **更新コマンド** | `rails upcoming_events:aggregation` | `rails statistics:aggregation` |
27+
| **更新頻度** | 毎日 21:00 UTC | 毎週月曜 01:00 UTC |
28+
| **データの特性** | 頻繁に変更(キャンセル等) | 確定データ(変更なし) |
29+
| **レコード数** | 少ない(未来のみ) | 多い(全履歴) |
30+
31+
## 🔄 データフロー
32+
33+
### イベントのライフサイクル
34+
35+
```ruby
36+
# 例:1月15日に開催予定のイベント
37+
38+
# 1月1日時点(未来)
39+
UpcomingEvent.create!(
40+
event_id: 12345,
41+
event_name: "CoderDojo札幌",
42+
event_date: "2025-01-15",
43+
participants_limit: 20
44+
)
45+
46+
# 1月16日(イベント終了後)
47+
# UpcomingEventから削除され、EventHistoryに記録
48+
EventHistory.create!(
49+
event_id: 12345,
50+
dojo_name: "CoderDojo札幌",
51+
evented_at: "2025-01-15",
52+
participants: 15 # 実際の参加者数
53+
)
54+
```
55+
56+
### キャンセルされたイベントの扱い
57+
58+
```ruby
59+
# 重要:キャンセルされたイベントは統計に含まれない
60+
61+
# 1月10日:大雪で1月15日のイベントがキャンセル
62+
UpcomingEvent.find_by(event_id: 12345).destroy
63+
64+
# 結果:
65+
# - UpcomingEventから削除 ✓
66+
# - EventHistoryには記録されない ✓
67+
# - 統計(/stats)には反映されない ✓
68+
```
69+
70+
## 💡 設計の利点
71+
72+
### 1. 統計の正確性
73+
74+
実際に開催されたイベントのみが `EventHistory` に記録されるため、統計データが正確です。
75+
76+
```ruby
77+
# 統計ページ(/stats)の集計
78+
EventHistory.where(dojo_name: "CoderDojo札幌").count
79+
# => 実際に開催されたイベント数のみ
80+
```
81+
82+
### 2. パフォーマンスの最適化
83+
84+
```ruby
85+
# UpcomingEvents:小さいテーブル
86+
UpcomingEvent.all # 高速(未来のイベントのみ)
87+
88+
# EventHistory:大きいが変更が少ない
89+
EventHistory.where(year: 2024) # インデックスで高速化
90+
```
91+
92+
### 3. データの整合性
93+
94+
時間軸で自然に分離されるため、同じイベントが両方のテーブルに存在することがありません。
95+
96+
```ruby
97+
# 重複チェック(常にfalse)
98+
upcoming_ids = UpcomingEvent.pluck(:event_id)
99+
history_ids = EventHistory.pluck(:event_id)
100+
(upcoming_ids & history_ids).any? # => false
101+
```
102+
103+
### 4. 運用の柔軟性
104+
105+
```ruby
106+
# UpcomingEventsは頻繁に更新可能
107+
rake upcoming_events:aggregation # 毎日実行してもOK
108+
109+
# EventHistoryは保護されたデータ
110+
rake statistics:aggregation # 週1回で十分
111+
```
112+
113+
## 🛠️ 実装詳細
114+
115+
### UpcomingEvents の更新処理
116+
117+
```ruby
118+
# lib/upcoming_events/aggregation.rb
119+
class UpcomingEvents::Aggregation
120+
def run
121+
# 1. 古いデータを削除
122+
UpcomingEvent.delete_all
123+
124+
# 2. 各プロバイダから未来のイベントを取得
125+
fetch_from_connpass
126+
fetch_from_doorkeeper
127+
128+
# 3. DBに保存(過去のイベントは除外)
129+
events.select { |e| e[:date] > Date.today }.each do |event|
130+
UpcomingEvent.create!(event)
131+
end
132+
end
133+
end
134+
```
135+
136+
### EventHistory の集計処理
137+
138+
```ruby
139+
# lib/statistics/aggregation.rb
140+
class Statistics::Aggregation
141+
def run
142+
# 指定期間の過去イベントのみを集計
143+
period = @from..@to # 過去の期間
144+
145+
# 各プロバイダから過去のイベントを取得
146+
fetch_past_events(period)
147+
148+
# EventHistoryに保存(実際に開催されたもののみ)
149+
events.each do |event|
150+
EventHistory.create!(event) if event[:status] == 'held'
151+
end
152+
end
153+
end
154+
```
155+
156+
## 🔍 トラブルシューティング
157+
158+
### Q: 過去のイベントが統計に表示されない
159+
160+
A: `EventHistory` にデータが存在するか確認:
161+
162+
```bash
163+
# 特定期間のデータを再取得
164+
rails statistics:aggregation[202501,202501,doorkeeper]
165+
```
166+
167+
### Q: 近日開催に表示されるべきイベントが出ない
168+
169+
A: `UpcomingEvents` の更新を確認:
170+
171+
```bash
172+
# 手動で更新
173+
rails upcoming_events:aggregation
174+
```
175+
176+
### Q: 同じイベントが重複して表示される
177+
178+
A: 通常は起こりませんが、確認方法:
179+
180+
```ruby
181+
# Rails console で確認
182+
event_id = 12345
183+
UpcomingEvent.where(event_id: event_id).count # 0 or 1
184+
EventHistory.where(event_id: event_id).count # 0 or 1
185+
```
186+
187+
## 📊 実例:2025年1月のDoorkeeper問題
188+
189+
### 問題の発見
190+
191+
```ruby
192+
# SymbolとStringのキー不一致により、Doorkeeperイベントが保存されていなかった
193+
# lib/statistics/tasks/doorkeeper.rb
194+
e['id'] # nil(実際はe[:id])
195+
```
196+
197+
### 影響範囲の調査
198+
199+
```sql
200+
-- 3ヶ月分のデータが欠損
201+
SELECT COUNT(*) FROM event_histories
202+
WHERE service_name = 'doorkeeper'
203+
AND evented_at BETWEEN '2025-05-01' AND '2025-07-31';
204+
-- => 0(本来は71イベント)
205+
```
206+
207+
### 復旧作業
208+
209+
```bash
210+
# 月ごとに慎重に復旧
211+
rails statistics:aggregation[202505,202505,doorkeeper] # 24イベント復旧
212+
rails statistics:aggregation[202506,202506,doorkeeper] # 26イベント復旧
213+
rails statistics:aggregation[202507,202507,doorkeeper] # 21イベント復旧
214+
```
215+
216+
### 重要な発見
217+
218+
- `UpcomingEvents` は影響なし(別の処理系統)
219+
- `EventHistory` のみ影響(統計データ)
220+
- この分離設計により、影響範囲が限定的だった
221+
222+
## 🎯 まとめ
223+
224+
この2テーブル設計により:
225+
226+
1. **正確な統計** - 実際に開催されたイベントのみカウント
227+
2. **高いパフォーマンス** - 用途に応じた最適化
228+
3. **データの一貫性** - 時間軸での自然な分離
229+
4. **保守性** - 明確な責任分離
230+
231+
という利点を実現しています。

0 commit comments

Comments
 (0)