Skip to content

Commit e1e055b

Browse files
committed
perf: performance-driven re-design
BREAKING CHANGE: This release introduces significant performance improvements that change the internal storage format and key structure. Checkpoints created with earlier versions are incompatible with v0.1.0. Key performance improvements: - Replace some FT.SEARCH operations with sorted sets for write tracking - Add checkpoint-based key registry eliminating expensive SCAN/KEYS operations - Implement multi-level caching for frequently accessed keys and data - Optimize batch operations with pipelined Redis commands - Add lazy TTL refresh to reduce unnecessary operations - Improve index schemas for better query performance Architectural changes: - New CheckpointKeyRegistry tracks writes per checkpoint using sorted sets - Cached key generation methods reduce string concatenation overhead - Batch loading methods for pending writes and sends - Optimized get_tuple with direct document access patterns - Improved TTL management with threshold-based refresh Testing improvements: - Add comprehensive test coverage for new registry functionality - Test TTL behaviors, caching mechanisms, and error paths - Add integration tests for blob handling and metadata operations - Improve test isolation using unique thread IDs instead of flushdb The new architecture provides: - 50-70% reduction in Redis operations for typical workflows - Better scalability with checkpoint-scoped write tracking - Reduced memory footprint through efficient caching - Improved cluster mode compatibility
1 parent 8e87eba commit e1e055b

36 files changed

+7280
-1405
lines changed

CLAUDE.md

Lines changed: 102 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,87 @@
22

33
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
44

5+
## How to Write Tests for Coverage
6+
7+
When improving test coverage, follow these principles:
8+
9+
1. **Focus on Integration Tests**: Write tests that use real Redis instances and test actual usage scenarios. Unit tests are secondary to integration tests.
10+
11+
2. **Test What Code SHOULD Do**: Don't write tests that mirror what the code currently does. Test against the expected behavior and requirements.
12+
13+
3. **Use Meaningful Test Names**: Test names should describe the behavior being tested, not generic names like "test_function_x".
14+
15+
4. **Research Before Writing**: Find and understand existing tests for the feature/area before adding new tests.
16+
17+
5. **Test Error Paths and Edge Cases**: Focus on uncovered error handling, boundary conditions, and edge cases.
18+
19+
6. **Run Tests Incrementally**: Run `make test-all` after every 5 tests to ensure no regressions.
20+
21+
7. **Avoid "Ugly Mirror" Testing**: Don't create tests that simply verify the current implementation. Test the contract and expected behavior.
22+
23+
Example of a good integration test for error handling:
24+
```python
25+
def test_malformed_base64_blob_handling(redis_url: str) -> None:
26+
"""Test handling of malformed base64 data in blob decoding."""
27+
with _saver(redis_url) as saver:
28+
# Set up real scenario
29+
# Test error condition
30+
# Verify graceful handling
31+
```
32+
33+
## CRITICAL: Always Use TestContainers for Redis
34+
35+
**NEVER use Docker directly or manually start Redis containers!** All tests, benchmarks, and profiling scripts MUST use TestContainers. The library handles container lifecycle automatically.
36+
37+
```python
38+
from testcontainers.redis import RedisContainer
39+
40+
# Use redis:8 (has all required modules) or redis/redis-stack-server:latest
41+
redis_container = RedisContainer("redis:8")
42+
redis_container.start()
43+
try:
44+
redis_url = f"redis://{redis_container.get_container_host_ip()}:{redis_container.get_exposed_port(6379)}"
45+
# Use redis_url...
46+
finally:
47+
redis_container.stop()
48+
```
49+
550
## Development Commands
651

752
### Setup and Dependencies
853

954
```bash
1055
poetry install --all-extras # Install all dependencies with poetry (from README)
11-
make redis-start # Start Redis Stack container (includes RedisJSON and RediSearch)
12-
make redis-stop # Stop Redis container
1356
```
1457

1558
### Testing
1659

1760
```bash
18-
make test # Run tests with verbose output
19-
make test-all # Run all tests including API tests
61+
make test-all # PREFERRED: Run all tests including API tests when evaluating changes
62+
make test # Run tests with verbose output
63+
make test-coverage # Run tests with coverage
64+
make coverage-report # Show coverage report in terminal
65+
make coverage-html # Generate HTML coverage report
2066
pytest tests/test_specific.py # Run specific test file
2167
pytest tests/test_specific.py::test_function # Run specific test
2268
pytest --run-api-tests # Include API integration tests
2369
```
2470

71+
**Important**: Always use `make test-all` when evaluating changes to ensure all tests pass, including API integration tests.
72+
73+
Note: Tests automatically use TestContainers for Redis - do not manually start Redis containers.
74+
2575
### Code Quality
2676

2777
```bash
2878
make format # Format code with black and isort
2979
make lint # Run formatting, type checking, and other linters
3080
make check-types # Run mypy type checking
3181
make check # Run both linting and tests
82+
make find-dead-code # Find unused code with vulture
83+
poetry run check-format # Check formatting without modifying
84+
poetry run check-sort-imports # Check import sorting
85+
poetry run check-lint # Run all linting checks
3286
```
3387

3488
### Development
@@ -37,6 +91,19 @@ make check # Run both linting and tests
3791
make clean # Remove cache and build artifacts
3892
```
3993

94+
## Code Style Guidelines
95+
96+
- Use Black for formatting with target versions py39-py313
97+
- Sort imports with isort (black profile)
98+
- Strict typing required (disallow_untyped_defs=True)
99+
- Follow PEP 8 naming conventions (snake_case for functions/variables)
100+
- Type annotations required for all function parameters and return values
101+
- Explicit error handling with descriptive error messages
102+
- Test all functionality with both sync and async variants
103+
- Maintain test coverage with pytest
104+
- Use contextlib for resource management
105+
- Document public APIs with docstrings
106+
40107
## Architecture Overview
41108

42109
### Core Components
@@ -47,6 +114,8 @@ make clean # Remove cache and build artifacts
47114
- `__init__.py`: `RedisSaver` - Standard sync implementation
48115
- `aio.py`: `AsyncRedisSaver` - Async implementation
49116
- `shallow.py` / `ashallow.py`: Shallow variants that store only latest checkpoint
117+
- `key_registry.py`: Checkpoint key registry using sorted sets for efficient write tracking
118+
- `scan_utils.py`: Utilities for efficient key scanning and pattern matching
50119

51120
**Stores** (`langgraph/store/redis/`):
52121

@@ -82,10 +151,36 @@ Tests are organized by functionality:
82151

83152
- `test_sync.py` / `test_async.py`: Core checkpoint functionality
84153
- `test_store.py` / `test_async_store.py`: Store operations
85-
- `test_cluster_mode.py`: Redis Cluster specific tests
86-
- `test_*_ttl.py`: TTL functionality
87-
- `test_key_parsing.py`: Key generation and parsing logic
154+
- `test_cluster_mode.py` / `test_async_cluster_mode.py`: Redis Cluster specific tests
155+
- `test_checkpoint_ttl.py`: TTL functionality for checkpoints
156+
- `test_key_parsing.py` / `test_subgraph_key_parsing.py`: Key generation and parsing logic
88157
- `test_semantic_search_*.py`: Vector search capabilities
158+
- `test_interruption.py` / `test_streaming*.py`: Advanced workflow tests
159+
- `test_shallow_*.py`: Shallow checkpoint implementation tests
160+
- `test_decode_responses.py`: Redis response decoding tests
161+
- `test_crossslot_integration.py`: Cross-slot operation tests
162+
163+
## Notebooks and Examples
164+
165+
The `examples/` directory contains Jupyter notebooks demonstrating Redis integration with LangGraph:
166+
167+
- All notebooks MUST use Redis implementations (RedisSaver, RedisStore), not in-memory equivalents
168+
- Notebooks can be run via Docker Compose: `cd examples && docker compose up`
169+
- Each notebook includes installation of required dependencies within the notebook cells
170+
- TestContainers should be used for any new notebook examples requiring Redis
171+
172+
### Running Notebooks
173+
174+
1. With Docker (recommended):
175+
```bash
176+
cd examples
177+
docker compose up
178+
```
179+
180+
2. Locally:
181+
- Ensure Redis is running with required modules (RedisJSON, RediSearch)
182+
- Install dependencies: `pip install langgraph-checkpoint-redis jupyter`
183+
- Run: `jupyter notebook`
89184

90185
### Important Dependencies
91186

Makefile

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,25 @@ check-types:
1717
poetry run check-mypy
1818

1919
lint: format check-types
20-
20+
2121
test:
2222
poetry run test-verbose
2323

2424
test-all:
2525
poetry run test-verbose --run-api-tests
2626

27+
test-coverage:
28+
poetry run test-coverage
29+
30+
coverage-report:
31+
poetry run coverage-report
32+
33+
coverage-html:
34+
poetry run coverage-html
35+
36+
find-dead-code:
37+
poetry run find-dead-code
38+
2739
check: lint test
2840

2941
clean:

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,15 @@ The project includes several make commands for development:
349349
make check-types # Run mypy type checking
350350
```
351351

352+
- **Code Quality**:
353+
354+
```bash
355+
make test-coverage # Run tests with coverage reporting
356+
make coverage-report # Generate coverage report without running tests
357+
make coverage-html # Generate HTML coverage report (opens in htmlcov/)
358+
make find-dead-code # Find unused code with vulture
359+
```
360+
352361
- **Redis for Development/Testing**:
353362

354363
```bash

0 commit comments

Comments
 (0)