Investigation: Django bulk_update performance issues
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 25/100
- Loại issue
- Tính năng
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Đình trệ
- Lĩnh vực
- backend, databases, performance
Hướng nghiên cứu
Bắt đầu với morango/sync/stream/serialize.py, đặc biệt là WriteSink.consume() và các lệnh gọi save() riêng lẻ xung quanh dòng 315-317 và 333-335. Hãy tạo prototype và benchmark một thao tác cập nhật dựa trên VALUES so với cách tiếp cận hiện tại trên SQLite và PostgreSQL, sau đó ghi lại khả năng tương thích với cơ sở dữ liệu, hành vi của kích thước batch, các yêu cầu fallback và quyết định tích hợp.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Problem Statement
In morango/sync/stream/serialize.py, there are comments indicating that Django's bulk_update performs poorly. The current implementation falls back to individual save() calls for store and counter updates, which is inefficient for large datasets.
Current Implementation Issues
The WriteSink.consume() method currently:
- Uses
bulk_createwithignore_conflicts=Truefor initial creation - Falls back to individual
save()calls for updates (lines 315-317, 333-335) - Has TODO comments asking "bulk_update performs poorly-- is there a better way?"
Research Findings
Django bulk_update Performance
- Django's
bulk_updategenerates largeCASE(WHEN(...))statements - Performance degrades quadratically with dataset size
- For 100,000 records: ~647 seconds with Django's bulk_update
Alternative Approaches
1. django-fast-update Library
The django-fast-update library demonstrates significant performance improvements:
Key Features:
- Uses
UPDATE FROM VALUESSQL pattern - 8x+ faster than Django's bulk_update
- Linear performance scaling vs quadratic
- Supports SQLite 3.15+, PostgreSQL, MariaDB 10.2+, MySQL 5.7+
- Falls back gracefully for unsupported databases
Performance Comparison:
| Method | 100k records | Notes |
|---|---|---|
| Django bulk_update | ~647s | Quadratic scaling |
| django-fast-update | ~80s | Linear scaling |
| copy_update (PG only) | ~40s | Best for large datasets |
2. SQL VALUES Approach
UPDATE target_table
SET field1 = source.field1, field2 = source.field2
FROM (VALUES
(id1, val1a, val1b),
(id2, val2a, val2b)
) AS source(id, field1, field2)
WHERE target_table.id = source.id
3. TEMP TABLE Approach
-- Create temp table with update data
CREATE TEMP TABLE temp_updates AS
SELECT id, new_field1, new_field2 FROM source_data;
-- Perform join-based update
UPDATE target_table
SET field1 = temp_updates.new_field1,
field2 = temp_updates.new_field2
FROM temp_updates
WHERE target_table.id = temp_updates.id;
Proposed Solution
Create a vendored utility module morango.utils.bulk_update that:
1. Implements VALUES-based approach:
def fast_bulk_update(model_class, objects, fields, batch_size=500):
"""
Perform bulk update using VALUES approach.
Falls back to Django's bulk_update if VALUES not supported.
"""
if not objects:
return
# Try VALUES approach first
try:
return _values_based_update(model_class, objects, fields, batch_size)
except (NotImplementedError, DatabaseError):
# Fall back to Django's implementation
model_class.objects.bulk_update(objects, fields, batch_size=batch_size)
2. Database-Specific Implementations:
- SQLite/PostgreSQL: VALUES approach
- Other databases: Graceful fallback to Django's bulk_update
3. Batch Processing:
- Process in configurable batch sizes (default 500)
- Handle SQLite variable limits automatically
- Memory-efficient streaming for large datasets
Implementation Plan
-
Phase 1 - Research & Prototyping
- Create proof-of-concept VALUES implementation
- Benchmark against current approach
- Test with SQLite and PostgreSQL
-
Phase 2 - Integration
- Replace individual
save()calls inWriteSink.consume() - Add configuration for batch size
- Maintain backward compatibility
- Replace individual
-
Phase 3 - Testing & Optimization
- Performance testing with realistic datasets
- Edge case handling (empty datasets, single records)
- Database compatibility testing
Expected Benefits
- Performance: 5-10x improvement for bulk updates
- Scalability: Linear vs quadratic complexity
- Maintainability: Cleaner code without individual save() loops
- Compatibility: Works with existing Django models
Open Questions
- Should we implement both VALUES and TEMP TABLE approaches?
- What batch size works best for our typical workloads?
- Are there any Morango-specific constraints we need to consider?
- Should this be a separate utility or integrated directly into the sync pipeline?
References
- https://github.com/learningequality/morango/pull/287#discussion_r2914828484
- django-fast-update
- Django bulk_update PR #9606
- django-bulk-load
- django-pg-bulk-update
Context
Branch: release-v0.9.x
AI usage
This issue was written by AI after it did some research, at my prompting.
- Ngôn ngữ chính
- Python
- Star
- 15
- Fork
- 23
- Merge trung bình
- 1 ngày 22 giờ
- Pull request đã merge (30 ngày)
- 3
Chuẩn bị môi trường
- Có Dockerfile hoặc tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của learningequality/morango
-
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 58/100
learningequality/morango#349 ·
-
Model hierarchies using self-referential non-deferrable FKs can cause deletions to fail deserializationCó thể làm lại được @bjester đã nhận 94 ngày trước và không có pull request nào đang mở. Đang mởDEV: backend P0 - critical
learningequality/morango#340 · 1 người được giao ·
-
Implement DeserializeTask and StoreModelSource for streaming deserializationCó thể làm lại được @bjester đã nhận 166 ngày trước và không có pull request nào đang mở. Đang mởDEV: backend TAG: new feature TAG: performance
learningequality/morango#317 · 1 người được giao ·
-
P1 - important
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 45/100
learningequality/morango#308 · 1 bình luận ·
-
Serialization into store does not update _self_ref_fk for existing recordsCó thể làm lại được @bjester đã nhận 94 ngày trước và không có pull request nào đang mở. Đang mởDEV: backend P0 - critical
learningequality/morango#305 · 1 người được giao ·
Tất cả issue của learningequality/morango
Issue tương tự
-
bug status/needs-triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 86/100
prowler-cloud/prowler#12887 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
area: desktop platform: macos priority: p3 status: ready type: enhancement
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 92/100
use-agent-os/agent-os#3484 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 86/100
open-telemetry/opentelemetry-python-contrib#5113 · 2 bình luận · 2 reaction ·
Maintainer thường phản hồi trong vòng 1 ngày
-
external
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
langchain-ai/docs#6255 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày