Yuki Takei 6 месяцев назад
Родитель
Сommit
fc80566022
1 измененных файлов с 0 добавлено и 210 удалено
  1. 0 210
      .serena/memories/socketio-update-desc-count-optimization-plan.md

+ 0 - 210
.serena/memories/socketio-update-desc-count-optimization-plan.md

@@ -1,210 +0,0 @@
-# UpdateDescCount イベント最適化計画
-
-## 📋 分析サマリー
-
-### 現状の問題
-- **UpdateDescCount** イベントが **全クライアントへブロードキャスト** されている
-- 他のSocket.IOイベントはすべて適切にルーム機能を使用しているが、このイベントだけが取り残されている
-- 一括マイグレーション時に数千回のイベントが短時間で発生し、パフォーマンス問題を引き起こす
-
-### 他イベントとの比較
-
-| イベント名 | 配信範囲 | 実装方法 | 適切性 |
-|-----------|---------|---------|--------|
-| **UpdateDescCount** | ❌ 全ユーザー | `socket.emit()` | ❌ 不適切 |
-| **S2cMessagePageUpdated** | ✅ ページ閲覧者のみ | `.in(page:${pageId})` | ✅ 適切 |
-| **notificationUpdated** | ✅ 特定ユーザーのみ | `.in(user:${userId})` | ✅ 適切 |
-| **YjsAwarenessStateSizeUpdated** | ✅ ページ閲覧者のみ | `.in(page:${pageId})` | ✅ 適切 |
-| **YjsHasYdocsNewerThanLatestRevisionUpdated** | ✅ ページ閲覧者のみ | `.in(page:${pageId})` | ✅ 適切 |
-
----
-
-## 🎯 最適化方針
-
-### 推奨アプローチ: ルーム機能の導入
-
-影響を受ける祖先ページを閲覧しているユーザーにのみイベントを配信する。
-
----
-
-## 🔧 実装計画
-
-### Phase 1: ルーム機能の導入
-
-#### 1. `emitUpdateDescCount` メソッドの修正
-
-**ファイル**: `apps/app/src/server/service/page/index.ts`
-
-**現在の実装** (L3482-3486):
-```typescript
-private emitUpdateDescCount(data: UpdateDescCountRawData): void {
-  const socket = this.crowi.socketIoService.getDefaultSocket();
-
-  socket.emit(SocketEventName.UpdateDescCount, data);
-}
-```
-
-**修正後**:
-```typescript
-private emitUpdateDescCount(data: UpdateDescCountRawData): void {
-  const socket = this.crowi.socketIoService.getDefaultSocket();
-  
-  // 各祖先ページを閲覧しているユーザーにのみ配信
-  Object.entries(data).forEach(([pageId, count]) => {
-    socket
-      .in(getRoomNameWithId(RoomPrefix.PAGE, pageId))
-      .emit(SocketEventName.UpdateDescCount, { [pageId]: count });
-  });
-}
-```
-
-**必要なインポート追加**:
-```typescript
-import { RoomPrefix, getRoomNameWithId } from '~/server/service/socket-io/helper';
-```
-
-#### 2. クライアント側の修正は不要
-
-クライアント側 (`apps/app/src/client/components/ItemsTree/ItemsTree.tsx`) は、データ構造が変わらないため修正不要。
-
----
-
-### Phase 2 (オプション): 一括処理時の最適化
-
-一括マイグレーションなど、大量のページを処理する場合にイベント送信を抑制するオプションを追加。
-
-#### `updateDescendantCountOfAncestors` メソッドの修正
-
-**ファイル**: `apps/app/src/server/service/page/index.ts`
-
-**修正後**:
-```typescript
-async updateDescendantCountOfAncestors(
-  pageId: ObjectIdLike, 
-  inc: number, 
-  shouldIncludeTarget: boolean,
-  suppressEmit: boolean = false  // 新規パラメータ
-): Promise<void> {
-  const Page = mongoose.model<IPage, PageModel>('Page');
-  const ancestors = await Page.findAncestorsUsingParentRecursively(pageId, shouldIncludeTarget);
-  const ancestorPageIds = ancestors.map(p => p._id);
-
-  await Page.incrementDescendantCountOfPageIds(ancestorPageIds, inc);
-
-  if (!suppressEmit) {
-    const updateDescCountData: UpdateDescCountRawData = Object.fromEntries(ancestors.map(p => [p._id.toString(), p.descendantCount + inc]));
-    this.emitUpdateDescCount(updateDescCountData);
-  }
-}
-```
-
-#### 一括処理での使用例
-
-`normalizeParentRecursivelySubOperation` メソッド内で:
-```typescript
-await this.updateDescendantCountOfAncestors(page._id, inc, false, true); // suppressEmit: true
-```
-
----
-
-## 📊 期待される効果
-
-### Phase 1 実装後
-
-#### 通常操作の場合
-- **Before**: 全クライアント(例: 100人)に配信 → 100回のクライアント処理
-- **After**: 関連ページ閲覧者のみ(例: 3人)に配信 → 3回のクライアント処理
-- **削減率**: 97%
-
-#### 一括マイグレーション(5,000ページ)の場合
-- **Before**: 5,000回 × 100クライアント = 500,000回のクライアント処理
-- **After**: 5,000回 × 平均3クライアント = 15,000回のクライアント処理
-- **削減率**: 97%
-
-### Phase 2 実装後(一括処理時)
-
-- **Before**: 5,000回のSocket.IO emit
-- **After**: 0回のSocket.IO emit(suppressEmit: true)
-- **削減率**: 100%
-
----
-
-## ✅ テスト項目
-
-### 機能テスト
-
-1. **ページ作成**
-   - [ ] 作成したページの祖先ページを閲覧しているユーザーにのみイベントが届く
-   - [ ] 無関係なページを閲覧しているユーザーにはイベントが届かない
-   - [ ] CountBadgeが正しく更新される
-
-2. **ページ削除**
-   - [ ] 削除したページの祖先ページを閲覧しているユーザーにのみイベントが届く
-   - [ ] CountBadgeが正しく減少する
-
-3. **ページ移動**
-   - [ ] 移動元と移動先の祖先ページを閲覧しているユーザーにイベントが届く
-   - [ ] 両方のCountBadgeが正しく更新される
-
-4. **複数タブでの動作**
-   - [ ] 同じページを複数タブで開いている場合、すべてのタブで更新される
-   - [ ] 異なるページを開いているタブでは、関連するページのみ更新される
-
----
-
-## 🎯 実装優先度
-
-### 高優先度: Phase 1(ルーム機能の導入)
-
-- **実装難易度**: 低
-- **影響範囲**: 限定的
-- **効果**: 大(97%削減)
-- **リスク**: 低
-- **推奨**: すぐに実装すべき
-
-### 中優先度: Phase 2(一括処理時の最適化)
-
-- **実装難易度**: 中
-- **影響範囲**: 中(一括処理のみ)
-- **効果**: 大(一括処理時のみ)
-- **リスク**: 低(suppressEmitフラグで制御)
-- **推奨**: Phase 1完了後、必要に応じて実装
-
----
-
-## 📚 参考実装
-
-### 正しいルーム使用の例
-
-**S2cMessagePageUpdated** (`apps/app/src/server/service/system-events/sync-page-status.ts`):
-```typescript
-socketIoService.getDefaultSocket()
-  .in(getRoomNameWithId(RoomPrefix.PAGE, page._id))
-  .except(getRoomNameWithId(RoomPrefix.USER, user._id))
-  .emit('page:update', { s2cMessagePageUpdated });
-```
-
-**notificationUpdated** (`apps/app/src/server/service/in-app-notification.ts`):
-```typescript
-this.socketIoService.getDefaultSocket()
-  .in(getRoomNameWithId(RoomPrefix.USER, userId))
-  .emit('notificationUpdated');
-```
-
----
-
-## 💡 まとめ
-
-### 推奨アクション
-
-1. **Phase 1を優先実装**
-   - ルーム機能の導入により、97%のパフォーマンス改善が期待できる
-   - リアルタイム性を維持
-   - リスクが低い
-
-2. **Phase 2は状況に応じて**
-   - 一括マイグレーションが頻繁に発生する場合のみ実装
-   - Phase 1で十分な効果が得られる可能性が高い
-
-3. **他のイベントとの整合性**
-   - UpdateDescCountを修正することで、すべてのSocket.IOイベントが適切にルーム機能を使用する統一された実装になる