Skip to content

Commit bef35f9

Browse files
Fix: Remove watcher_ids column usage from queries (#101)
* Fix: Remove watcher_ids column usage from queries The watcher_groups.watcher_ids column doesn't exist in the database, causing "Unknown column 'g.watcher_ids'" errors. Changes: - get_all_groups_with_watchers: Build watcher_ids dynamically from JOIN results instead of querying the non-existent column - batch_upsert_watchers: Remove watcher_ids column update, just update the group timestamp The watchers table is now the single source of truth for watcher IDs.
1 parent 8fd7a06 commit bef35f9

File tree

4 files changed

+423
-16
lines changed

4 files changed

+423
-16
lines changed

.env.backup

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# =============================================================================
2+
# Cosmic Sync Server Environment Variables - Test Configuration
3+
# =============================================================================
4+
5+
# =============================================================================
6+
# Database Configuration
7+
# =============================================================================
8+
DB_USER="root"
9+
DB_PASS="recognizer"
10+
DB_NAME="cosmic_sync"
11+
DB_HOST="127.0.0.1"
12+
DB_PORT=3306
13+
DB_POOL=5
14+
15+
# =============================================================================
16+
# Server Configuration
17+
# =============================================================================
18+
SERVER_HOST=0.0.0.0
19+
SERVER_PORT=50051
20+
WORKER_THREADS=4
21+
HEARTBEAT_INTERVAL_SECS=30
22+
AUTH_TOKEN_EXPIRY_HOURS=24
23+
24+
# OAuth Configuration
25+
OAUTH_CLIENT_ID=cosmic-sync
26+
OAUTH_CLIENT_SECRET=cosmicsecretsocmicsecret
27+
OAUTH_REDIRECT_URI=http://10.241.62.167:8080/oauth/callback
28+
OAUTH_AUTH_URL=http://10.241.62.167:4000/oauth/authorize
29+
OAUTH_TOKEN_URL=http://10.241.62.167:4000/oauth/token
30+
OAUTH_USER_INFO_URL=http://10.241.62.167:4000/api/settings
31+
OAUTH_SCOPE=profile:read
32+
33+
# Request Limits
34+
MAX_CONCURRENT_REQUESTS=100
35+
MAX_FILE_SIZE=52428800
36+
37+
# Logging Configuration
38+
RUST_LOG=cosmic_sync_server=debug,info
39+
LOG_LEVEL=info
40+
LOG_TO_FILE=true
41+
LOG_FILE=logs/cosmic-sync-server.log
42+
LOG_MAX_FILE_SIZE=10485760
43+
LOG_MAX_BACKUPS=5
44+
45+
# =============================================================================
46+
# Storage Configuration - S3 MinIO
47+
# =============================================================================
48+
STORAGE_TYPE="s3"
49+
AWS_REGION="us-east-2"
50+
S3_BUCKET="cosmic-sync-files"
51+
S3_KEY_PREFIX="files/"
52+
AWS_ACCESS_KEY_ID="minioadmin"
53+
AWS_SECRET_ACCESS_KEY="minioadmin"
54+
S3_ENDPOINT_URL="http://127.0.0.1:9000"
55+
S3_FORCE_PATH_STYLE="true"
56+
S3_TIMEOUT_SECONDS="30"
57+
S3_MAX_RETRIES="3"
58+
59+
# =============================================================================
60+
# Development Mode
61+
# =============================================================================
62+
COSMIC_SYNC_DEV_MODE="1"
63+
COSMIC_SYNC_TEST_MODE="1"
64+
COSMIC_SYNC_DEBUG_MODE="1"
65+
66+
# # Feature Flags
67+
# COSMIC_SYNC_TEST_MODE=false
68+
# COSMIC_SYNC_DEBUG_MODE=false
69+
# ENABLE_METRICS=false
70+
# STORAGE_ENCRYPTION=true
71+
# REQUEST_VALIDATION=true
72+
73+
SERVER_ENCODE_KEY=c3e15e2f727cf777380f23a9f9fa8156c5f4f7f3e697f6dc95a47372e76ac6bf
74+
75+
# OFF
76+
RABBITMQ_ENABLED=false
77+
78+
# ON
79+
# RABBITMQ_ENABLED=true
80+
# RABBITMQ_URL=amqp://guest:guest@127.0.0.1:5672/%2f
81+
# RABBITMQ_EXCHANGE=cosmic.sync
82+
# RABBITMQ_QUEUE_PREFIX=cosmic
83+
# RABBITMQ_PREFETCH=64
84+
# RABBITMQ_DURABLE=true

CLIENT_RETRY_POLICY.md

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
# BatchOperations 에러 처리 정책 - 클라이언트/서버 연동
2+
3+
**작성일**: 2025-12-10
4+
**최종 업데이트**: 2025-12-11
5+
**대상**: cosmic-sync-server 팀, cosmic-sync 클라이언트 팀
6+
7+
---
8+
9+
## 1. 개요
10+
11+
서버에서 제공한 권장사항을 반영하여 클라이언트 재시도 정책을 개선했습니다.
12+
이 문서는 **서버-클라이언트 간 에러 처리 연동 규약**을 정의합니다.
13+
14+
### 1.1 주요 변경사항 (2025-12-11)
15+
16+
| 구분 | 이전 | 이후 |
17+
|------|------|------|
18+
| **영구적 에러 재시도** | 1회 | **0회** (즉시 실패) |
19+
| **일시적 에러 재시도** | 무제한 | **10회** |
20+
| **충돌 에러 재시도** | 무제한 | **3회** (싱크 후) |
21+
| **에러 분류** | 메시지 파싱 | **error_code 우선** |
22+
| **Delete 멱등성** | 미구현 | **구현 완료** |
23+
24+
### 1.2 서버 구현 완료 항목
25+
26+
| 작업 | 상태 | 파일 |
27+
|------|------|------|
28+
| Delete 멱등성 구현 | ✅ 완료 | `src/handlers/file/delete.rs:275-287` |
29+
| Batch 에러 코드 개선 | ✅ 완료 | `src/handlers/file/batch.rs:270-302` |
30+
| Proto ErrorCode 정의 | ✅ 완료 | `proto/sync.proto:598-633` |
31+
| conflict_resolution 필드 | ✅ 완료 | `proto/sync.proto:581-586` |
32+
33+
---
34+
35+
## 2. ErrorCode 기반 에러 분류
36+
37+
### 2.1 클라이언트 구현: `classify_error()`
38+
39+
```rust
40+
pub enum ErrorCategory {
41+
Success,
42+
AlreadyDone,
43+
Transient { max_retries: u32 },
44+
Conflict { max_retries: u32 },
45+
Permanent { notify_user: bool, reason: &'static str },
46+
}
47+
48+
pub fn classify_error(error_code: i32) -> ErrorCategory {
49+
match error_code {
50+
0 => ErrorCategory::Success, // SUCCESS
51+
31 => ErrorCategory::AlreadyDone, // FILE_ALREADY_DELETED
52+
53+
70 | 90 | 1 => ErrorCategory::Transient { max_retries: 10 }, // STORAGE, DB, UNKNOWN
54+
50 | 53 | 54 => ErrorCategory::Conflict { max_retries: 3 }, // REVISION, CONCURRENT, STALE
55+
56+
10 | 11 | 12 => ErrorCategory::Permanent { notify_user: true, reason: "Authentication" },
57+
51 => ErrorCategory::Permanent { notify_user: true, reason: "Path conflict" },
58+
71 | 72 => ErrorCategory::Permanent { notify_user: true, reason: "Quota exceeded" },
59+
21 | 20 | 22 | 23 => ErrorCategory::Permanent { notify_user: false, reason: "Invalid request" },
60+
91 => ErrorCategory::Permanent { notify_user: false, reason: "Schema error" },
61+
62+
30 => ErrorCategory::Transient { max_retries: 5 }, // FILE_NOT_FOUND
63+
_ => ErrorCategory::Transient { max_retries: 5 }, // Unknown codes
64+
}
65+
}
66+
```
67+
68+
### 2.2 ErrorCode 참조 테이블
69+
70+
| ErrorCode || 용도 | 클라이언트 동작 |
71+
|-----------|-----|------|----------------|
72+
| **성공** | | | |
73+
| `SUCCESS` | 0 | 정상 완료 | 큐에서 제거 |
74+
| `FILE_ALREADY_DELETED` | 31 | 이미 삭제됨 | 성공으로 처리, 큐에서 제거 |
75+
| **일시적 에러** | | | |
76+
| `STORAGE_FAILED` | 70 | 저장소 오류 | 최대 10회 재시도 |
77+
| `DB_ERROR` | 90 | DB 오류 | 최대 10회 재시도 |
78+
| `UNKNOWN_ERROR` | 1 | 알 수 없는 오류 | 최대 10회 재시도 |
79+
| **충돌 에러** | | | |
80+
| `REVISION_CONFLICT` | 50 | 리비전 불일치 | 싱크 후 최대 3회 재시도 |
81+
| `CONCURRENT_MODIFICATION` | 53 | 동시 수정 | 싱크 후 최대 3회 재시도 |
82+
| `STALE_OPERATION` | 54 | 오래된 작업 | 싱크 후 최대 3회 재시도 |
83+
| **영구적 에러 (사용자 알림)** | | | |
84+
| `AUTH_FAILED` | 10 | 인증 실패 | 즉시 실패, 사용자 알림 |
85+
| `AUTH_TOKEN_INVALID` | 11 | 토큰 만료 | 즉시 실패, 사용자 알림 |
86+
| `PATH_CONFLICT` | 51 | 경로 충돌 | 즉시 실패, 사용자 알림 |
87+
| `QUOTA_EXCEEDED` | 71 | 용량 초과 | 즉시 실패, 사용자 알림 |
88+
| **영구적 에러 (로그만)** | | | |
89+
| `INVALID_REQUEST` | 21 | 잘못된 요청 | 즉시 실패 |
90+
| `PATH_INVALID` | 23 | 잘못된 경로 | 즉시 실패 |
91+
| `DB_SCHEMA_ERROR` | 91 | DB 스키마 오류 | 즉시 실패 |
92+
93+
---
94+
95+
## 3. Delete 작업 멱등성
96+
97+
### 3.1 클라이언트 구현
98+
99+
```rust
100+
pub fn is_delete_success_error_code(error_code: i32) -> bool {
101+
matches!(error_code, 0 | 30 | 31) // SUCCESS, FILE_NOT_FOUND, FILE_ALREADY_DELETED
102+
}
103+
```
104+
105+
### 3.2 서버 응답
106+
107+
**Delete 요청 시 파일이 이미 없는 경우:**
108+
- `success: true`, `error_code: 0` (SUCCESS) 반환
109+
- 클라이언트는 `error_code`가 0, 30, 31이면 성공으로 처리
110+
111+
---
112+
113+
## 4. 재시도 정책
114+
115+
### 4.1 재시도 상수
116+
117+
```rust
118+
const MAX_PERMANENT_ERROR_RETRIES: u32 = 0; // 영구적 에러: 즉시 실패
119+
const MAX_TRANSIENT_ERROR_RETRIES: u32 = 10; // 일시적 에러: 최대 10회
120+
const MAX_CONFLICT_ERROR_RETRIES: u32 = 3; // 충돌 에러: 최대 3회
121+
```
122+
123+
### 4.2 백오프 전략
124+
125+
```rust
126+
const INITIAL_DELAY_MS: u64 = 1000; // 1초
127+
const MAX_DELAY_MS: u64 = 300_000; // 5분
128+
const BACKOFF_MULTIPLIER: f64 = 2.0;
129+
const JITTER_FACTOR: f64 = 0.1; // ±10% 랜덤 지터
130+
```
131+
132+
| 재시도 | 대기 시간 |
133+
|--------|----------|
134+
| 1 | 1초 |
135+
| 2 | 2초 |
136+
| 3 | 4초 |
137+
| ... | ... |
138+
| 10 | 300초 (5분, 최대) |
139+
140+
---
141+
142+
## 5. 사용자 결정 기반 재시도 시스템
143+
144+
`notify_user: true` 에러 발생 시 **AwaitingUserDecision** 상태로 전환됩니다.
145+
146+
### 5.1 사용자 액션
147+
148+
| 에러 코드 | 가능한 액션 |
149+
|-----------|------------|
150+
| `PATH_CONFLICT` (51) | Overwrite, Rename, Skip |
151+
| `QUOTA_EXCEEDED` (71) | Retry, Skip |
152+
| `AUTH_*` (10-12) | Retry, Skip |
153+
154+
### 5.2 Overwrite 지원
155+
156+
Proto에 `ConflictInfo.ResolutionStrategy`가 정의되어 있음:
157+
```protobuf
158+
enum ResolutionStrategy {
159+
MANUAL = 0; // 기본값
160+
LAST_WRITE_WINS = 1; // 타임스탬프 기반
161+
SERVER_WINS = 2; // 서버 우선
162+
CLIENT_WINS = 3; // 클라이언트 우선 (= Overwrite)
163+
}
164+
```
165+
166+
**클라이언트 Overwrite 액션 = `conflict_resolution: CLIENT_WINS`로 요청 전송**
167+
168+
### 5.3 Stale 작업 자동 정리
169+
170+
동기화 후 더 이상 필요 없는 `AwaitingUserDecision` 작업은 자동으로 정리됩니다:
171+
172+
| 작업 유형 | 정리 조건 |
173+
|----------|----------|
174+
| Upload/Update | 서버에 동일/더 높은 리비전 존재 |
175+
| Delete | 서버에 해당 파일 없음 |
176+
| Download | 로컬에 동일/더 높은 리비전 존재 |
177+
178+
---
179+
180+
## 6. Proto 정의 참조
181+
182+
```protobuf
183+
enum ErrorCode {
184+
SUCCESS = 0;
185+
UNKNOWN_ERROR = 1;
186+
187+
// Authentication (10-19)
188+
AUTH_FAILED = 10;
189+
AUTH_TOKEN_INVALID = 11;
190+
AUTH_ACCOUNT_MISMATCH = 12;
191+
192+
// Validation (20-29)
193+
VALIDATION_FAILED = 20;
194+
INVALID_REQUEST = 21;
195+
FILE_SIZE_MISMATCH = 22;
196+
PATH_INVALID = 23;
197+
198+
// File Operations (30-49)
199+
FILE_NOT_FOUND = 30;
200+
FILE_ALREADY_DELETED = 31;
201+
FILE_ALREADY_EXISTS = 32;
202+
203+
// Conflicts (50-69)
204+
REVISION_CONFLICT = 50;
205+
PATH_CONFLICT = 51;
206+
PATH_MISMATCH = 52;
207+
CONCURRENT_MODIFICATION = 53;
208+
STALE_OPERATION = 54;
209+
210+
// Storage (70-89)
211+
STORAGE_FAILED = 70;
212+
QUOTA_EXCEEDED = 71;
213+
FILE_SIZE_LIMIT = 72;
214+
215+
// Database (90-99)
216+
DB_ERROR = 90;
217+
DB_SCHEMA_ERROR = 91;
218+
}
219+
```
220+
221+
---
222+
223+
## 7. 구현 체크리스트
224+
225+
### 서버측 ✅ 완료
226+
- [x] Delete 멱등성: 이미 삭제된 파일 → `success: true` 반환
227+
- [x] Batch 에러 코드 개선: 에러 메시지 기반 ErrorCode 매핑
228+
- [x] conflict_resolution 필드 지원
229+
230+
### 클라이언트측
231+
- [x] `classify_error()` 함수 구현
232+
- [x] 재시도 횟수 제한 (영구:0, 일시:10, 충돌:3)
233+
- [x] Delete 멱등성 처리 (`is_delete_success_error_code`)
234+
- [x] `error_code` 우선 분류
235+
- [x] 사용자 결정 시스템 (`AwaitingUserDecision` 상태)
236+
- [x] Stale 작업 자동 정리
237+
- [ ] 프론트엔드 UI 통합
238+
239+
---
240+
241+
**문의**: 연동 관련 질문이 있으시면 연락주세요.

0 commit comments

Comments
 (0)