Skip to content

Commit 849b685

Browse files
jeonghanjooclaude
andcommitted
<ADD>: Async 지원 설계 및 계획 수립
- pymongo-async-tutorial.md: PyMongo async API 학습 자료 - PROGRESS.md: mongoengine async 지원 구현 계획서 - 통합된 Document 클래스 방식 채택 - async_ 접두사로 메서드 구분 - 5단계 구현 로드맵 수립 - CLAUDE.md: 프로젝트 개발 가이드 - async 작업 방식 문서화 - 브랜치 전략 및 PR 워크플로우 정의 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent e51ee40 commit 849b685

File tree

3 files changed

+602
-0
lines changed

3 files changed

+602
-0
lines changed

CLAUDE.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
MongoEngine is a Python Object-Document Mapper (ODM) for MongoDB. It provides a declarative API similar to Django's ORM but designed for document databases.
8+
9+
## Common Development Commands
10+
11+
### Testing
12+
```bash
13+
# Run all tests (requires MongoDB running locally on default port 27017)
14+
pytest tests/
15+
16+
# Run specific test file or directory
17+
pytest tests/fields/test_string_field.py
18+
pytest tests/document/
19+
20+
# Run tests with coverage
21+
pytest tests/ --cov=mongoengine
22+
23+
# Run a single test
24+
pytest tests/fields/test_string_field.py::TestStringField::test_string_field
25+
26+
# Run tests for all Python/PyMongo versions
27+
tox
28+
```
29+
30+
### Code Quality
31+
```bash
32+
# Run all pre-commit checks
33+
pre-commit run -a
34+
35+
# Auto-format code
36+
black .
37+
38+
# Sort imports
39+
isort .
40+
41+
# Run linter
42+
flake8
43+
```
44+
45+
### Development Setup
46+
```bash
47+
# Install development dependencies
48+
pip install -r requirements-dev.txt
49+
pip install -e .[test]
50+
51+
# Install pre-commit hooks
52+
pre-commit install
53+
```
54+
55+
## Architecture Overview
56+
57+
### Core Components
58+
59+
1. **Document Classes** (mongoengine/document.py):
60+
- `Document` - Top-level documents stored in MongoDB collections
61+
- `EmbeddedDocument` - Documents embedded within other documents
62+
- `DynamicDocument` - Documents with flexible schema
63+
- Uses metaclasses (`DocumentMetaclass`, `TopLevelDocumentMetaclass`) for class creation
64+
65+
2. **Field System** (mongoengine/fields.py):
66+
- Fields are implemented as descriptors
67+
- Common fields: StringField, IntField, ListField, ReferenceField, EmbeddedDocumentField
68+
- Custom validation and conversion logic per field type
69+
70+
3. **QuerySet API** (mongoengine/queryset/):
71+
- Chainable query interface similar to Django ORM
72+
- Lazy evaluation of queries
73+
- Support for aggregation pipelines
74+
- Query optimization and caching
75+
76+
4. **Connection Management** (mongoengine/connection.py):
77+
- Multi-database support with aliasing
78+
- Connection pooling handled by PyMongo
79+
- MongoDB URI and authentication support
80+
81+
### Key Design Patterns
82+
83+
- **Metaclass-based document definition**: Document structure is defined at class creation time
84+
- **Descriptor pattern for fields**: Enables validation and type conversion on attribute access
85+
- **Lazy loading**: ReferenceFields can be dereferenced on demand
86+
- **Signal system**: Pre/post hooks for save, delete, and bulk operations
87+
- **Query builder pattern**: Fluent interface for constructing MongoDB queries
88+
89+
### Testing Approach
90+
91+
- Tests mirror the package structure (e.g., `tests/fields/` for field tests)
92+
- Heavy use of fixtures defined in `tests/fixtures.py`
93+
- Tests require a running MongoDB instance
94+
- Matrix testing across Python versions (3.9-3.13) and PyMongo versions
95+
96+
### Code Style
97+
98+
- Black formatting (88 character line length)
99+
- isort for import sorting
100+
- flake8 for linting (max complexity: 47)
101+
- All enforced via pre-commit hooks
102+
103+
## Important Notes
104+
105+
- Always ensure MongoDB is running before running tests
106+
- When modifying fields or documents, check impact on both regular and dynamic document types
107+
- QuerySet modifications often require updates to both `queryset/queryset.py` and `queryset/manager.py`
108+
- New features should include tests and documentation updates
109+
- Backward compatibility is important - avoid breaking changes when possible
110+
111+
## Async Support Development Workflow
112+
113+
When working on async support implementation, follow this workflow:
114+
115+
1. **Branch Strategy**: Create a separate branch for each phase (e.g., `async-phase1-foundation`)
116+
2. **Planning**: Create `PROGRESS_{PHASE_NAME}.md` (e.g., `PROGRESS_FOUNDATION.md`) detailing the work for that phase
117+
3. **PR Creation**: Create a GitHub PR after initial planning commit
118+
4. **Communication**: Use PR comments for questions, blockers, and discussions
119+
5. **Completion Process**:
120+
- Delete the phase-specific PROGRESS file
121+
- Update main `PROGRESS.md` with completed work
122+
- Update `CLAUDE.md` with learnings for future reference
123+
- Finalize PR for review
124+
125+
### Current Async Implementation Strategy
126+
127+
- **Integrated Approach**: Adding async methods directly to existing Document classes
128+
- **Naming Convention**: Use `async_` prefix for all async methods (e.g., `async_save()`)
129+
- **Connection Detection**: Methods check connection type and raise errors if mismatched
130+
- **Backward Compatibility**: Existing sync code remains unchanged
131+
132+
### Key Design Decisions
133+
134+
1. **No Separate AsyncDocument**: Async methods are added to existing Document class
135+
2. **Explicit Async Methods**: Rather than automatic async/sync switching, use explicit method names
136+
3. **Connection Type Enforcement**: Runtime errors when using wrong method type with connection
137+
4. **Gradual Migration Path**: Projects can migrate incrementally by connection type

PROGRESS.md

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
# MongoEngine Async Support Implementation Progress
2+
3+
## 프로젝트 목표
4+
PyMongo의 AsyncMongoClient를 활용하여 MongoEngine에 완전한 비동기 지원을 추가하는 것
5+
6+
## 현재 상황 분석
7+
8+
### PyMongo Async 지원 현황
9+
- PyMongo는 `AsyncMongoClient`를 통해 완전한 비동기 지원 제공
10+
- 모든 주요 작업에 async/await 패턴 사용
11+
- `async for`를 통한 커서 순회 지원
12+
- 기존 동기 API와 병렬로 제공되어 호환성 유지
13+
14+
### MongoEngine 현재 구조의 문제점
15+
1. **동기적 설계**: 모든 데이터베이스 작업이 블로킹 방식으로 구현
16+
2. **디스크립터 프로토콜 한계**: ReferenceField의 lazy loading이 동기적으로만 가능
17+
3. **전역 상태 관리**: 스레드 로컬 스토리지 사용으로 비동기 컨텍스트 관리 어려움
18+
4. **QuerySet 체이닝**: 현재의 lazy evaluation이 async/await와 충돌
19+
20+
## 개선된 구현 전략
21+
22+
### 핵심 설계 원칙
23+
1. **단일 Document 클래스**: 별도의 AsyncDocument 대신 기존 Document에 비동기 메서드 추가
24+
2. **연결 타입 기반 동작**: async connection 사용 시 자동으로 비동기 동작
25+
3. **명확한 메서드 네이밍**: `async_` 접두사로 비동기 메서드 구분
26+
4. **완벽한 하위 호환성**: 기존 코드는 수정 없이 동작
27+
28+
### 1단계: 기반 구조 설계 (Foundation)
29+
30+
#### 1.1 하이브리드 연결 관리자
31+
```python
32+
# mongoengine/connection.py 수정
33+
- 기존 connect() 함수 유지
34+
- connect_async() 함수 추가로 AsyncMongoClient 연결
35+
- get_connection()이 연결 타입 자동 감지
36+
- contextvars로 비동기 컨텍스트 관리
37+
```
38+
39+
#### 1.2 Document 클래스 확장
40+
```python
41+
# mongoengine/document.py 수정
42+
class Document(BaseDocument):
43+
# 기존 동기 메서드 유지
44+
def save(self, ...):
45+
if is_async_connection():
46+
raise RuntimeError("Use async_save() with async connection")
47+
# 기존 로직
48+
49+
# 새로운 비동기 메서드 추가
50+
async def async_save(self, force_insert=False, validate=True, ...):
51+
if not is_async_connection():
52+
raise RuntimeError("Use save() with sync connection")
53+
# 비동기 저장 로직
54+
55+
async def async_delete(self, signal_kwargs=None, ...):
56+
# 비동기 삭제 로직
57+
58+
async def async_reload(self):
59+
# 비동기 새로고침 로직
60+
```
61+
62+
### 2단계: 핵심 CRUD 작업
63+
64+
#### 2.1 통합된 QuerySet
65+
```python
66+
# mongoengine/queryset/queryset.py 수정
67+
class QuerySet:
68+
# 기존 동기 메서드 유지
69+
def first(self):
70+
if is_async_connection():
71+
raise RuntimeError("Use async_first() with async connection")
72+
# 기존 로직
73+
74+
# 비동기 메서드 추가
75+
async def async_first(self):
76+
# 첫 번째 결과 반환
77+
78+
async def async_get(self, *q_args, **q_kwargs):
79+
# 단일 객체 조회
80+
81+
async def async_count(self):
82+
# 개수 반환
83+
84+
async def async_create(self, **kwargs):
85+
# 객체 생성 및 저장
86+
87+
def __aiter__(self):
88+
# 비동기 반복자
89+
```
90+
91+
### 3단계: 고급 기능
92+
93+
#### 3.1 필드의 비동기 지원
94+
```python
95+
# ReferenceField에 비동기 메서드 추가
96+
class ReferenceField(BaseField):
97+
# 기존 동기 lazy loading은 유지
98+
def __get__(self, instance, owner):
99+
if is_async_connection():
100+
# 비동기 컨텍스트에서는 Proxy 객체 반환
101+
return AsyncReferenceProxy(self, instance)
102+
# 기존 동기 로직
103+
104+
# 명시적 비동기 fetch 메서드
105+
async def async_fetch(self, instance):
106+
# 비동기 참조 로드
107+
```
108+
109+
#### 3.2 비동기 집계 작업
110+
```python
111+
class QuerySet:
112+
async def async_aggregate(self, pipeline):
113+
# 비동기 집계 파이프라인 실행
114+
115+
async def async_distinct(self, field):
116+
# 비동기 distinct 작업
117+
```
118+
119+
### 4단계: 신호 및 트랜잭션
120+
121+
#### 4.1 하이브리드 신호 시스템
122+
```python
123+
# 동기/비동기 모두 지원하는 신호
124+
class HybridSignal:
125+
def send(self, sender, **kwargs):
126+
if is_async_connection():
127+
return self.async_send(sender, **kwargs)
128+
# 기존 동기 신호 전송
129+
130+
async def async_send(self, sender, **kwargs):
131+
# 비동기 신호 핸들러 실행
132+
```
133+
134+
#### 4.2 비동기 트랜잭션
135+
```python
136+
# 비동기 트랜잭션 컨텍스트 매니저
137+
@asynccontextmanager
138+
async def async_run_in_transaction():
139+
# 비동기 트랜잭션 관리
140+
```
141+
142+
## 구현 로드맵
143+
144+
### Phase 1: 기본 구조 (2-3주)
145+
- [ ] 하이브리드 연결 관리자 구현 (connect_async, is_async_connection)
146+
- [ ] Document 클래스에 async_save(), async_delete() 메서드 추가
147+
- [ ] EmbeddedDocument 클래스에 비동기 메서드 추가
148+
- [ ] 비동기 단위 테스트 프레임워크 설정
149+
150+
### Phase 2: 쿼리 작업 (3-4주)
151+
- [ ] QuerySet에 비동기 메서드 추가 (async_first, async_get, async_count)
152+
- [ ] 비동기 반복자 (__aiter__) 구현
153+
- [ ] async_create(), async_update(), async_delete() 벌크 작업
154+
- [ ] 비동기 커서 관리 및 최적화
155+
156+
### Phase 3: 필드 및 참조 (2-3주)
157+
- [ ] ReferenceField에 async_fetch() 메서드 추가
158+
- [ ] AsyncReferenceProxy 구현
159+
- [ ] LazyReferenceField 비동기 지원
160+
- [ ] GridFS 비동기 작업 (async_put, async_get)
161+
- [ ] 캐스케이드 작업 비동기화
162+
163+
### Phase 4: 고급 기능 (3-4주)
164+
- [ ] 하이브리드 신호 시스템 구현
165+
- [ ] async_run_in_transaction() 트랜잭션 지원
166+
- [ ] 비동기 컨텍스트 매니저 (async_switch_db 등)
167+
- [ ] async_aggregate() 집계 프레임워크 지원
168+
169+
### Phase 5: 통합 및 최적화 (2-3주)
170+
- [ ] 성능 최적화 및 벤치마크
171+
- [ ] 문서화 (async 메서드 사용법)
172+
- [ ] 마이그레이션 가이드 작성
173+
- [ ] 동기/비동기 통합 테스트
174+
175+
## 주요 고려사항
176+
177+
### 1. API 설계 원칙
178+
- **통합된 Document 클래스**: 별도 클래스 없이 기존 Document에 비동기 메서드 추가
179+
- **명명 규칙**: 비동기 메서드는 'async_' 접두사 사용 (예: save → async_save)
180+
- **연결 타입 자동 감지**: 연결 타입에 따라 적절한 메서드 사용 강제
181+
182+
### 2. 호환성 전략
183+
- 기존 코드는 100% 호환
184+
- 동기 연결에서 async 메서드 호출 시 명확한 에러
185+
- 비동기 연결에서 sync 메서드 호출 시 명확한 에러
186+
187+
### 3. 성능 고려사항
188+
- 연결 풀링 최적화
189+
- 배치 작업 지원
190+
- 불필요한 비동기 오버헤드 최소화
191+
192+
### 4. 테스트 전략
193+
- 모든 비동기 기능에 대한 단위 테스트
194+
- 동기/비동기 동작 일관성 검증
195+
- 연결 타입 전환 시나리오 테스트
196+
197+
## 예상 사용 예시
198+
199+
```python
200+
from mongoengine import Document, StringField, connect_async
201+
202+
# 비동기 연결
203+
await connect_async('mydatabase')
204+
205+
# 모델 정의 (기존과 완전히 동일)
206+
class User(Document):
207+
name = StringField(required=True)
208+
email = StringField(required=True)
209+
210+
# 비동기 사용
211+
user = User(name="John", email="[email protected]")
212+
await user.async_save()
213+
214+
# 비동기 조회
215+
user = await User.objects.async_get(name="John")
216+
users = await User.objects.filter(name__startswith="J").async_count()
217+
218+
# 비동기 반복
219+
async for user in User.objects.filter(active=True):
220+
print(user.name)
221+
222+
# 비동기 업데이트
223+
await User.objects.filter(name="John").async_update(email="[email protected]")
224+
225+
# ReferenceField 비동기 로드
226+
class Post(Document):
227+
author = ReferenceField(User)
228+
title = StringField()
229+
230+
post = await Post.objects.async_first()
231+
# 비동기 컨텍스트에서는 명시적 fetch 필요
232+
author = await post.author.async_fetch()
233+
```
234+
235+
## 다음 단계
236+
237+
1. **Phase 1 시작**: 하이브리드 연결 관리자 구현
238+
2. **커뮤니티 피드백**: 통합 설계 방식에 대한 의견 수렴
239+
3. **벤치마크 설정**: 동기/비동기 성능 비교 기준 수립
240+
4. **CI/CD 파이프라인**: 비동기 테스트 환경 구축
241+
242+
## 기대 효과
243+
244+
1. **완벽한 하위 호환성**: 기존 프로젝트는 수정 없이 동작
245+
2. **점진적 마이그레이션**: 필요한 부분만 비동기로 전환 가능
246+
3. **직관적 API**: async_ 접두사로 명확한 구분
247+
4. **성능 향상**: I/O 바운드 작업에서 크게 개선
248+
249+
---
250+
251+
이 문서는 구현 진행에 따라 지속적으로 업데이트됩니다.

0 commit comments

Comments
 (0)