quadkit-cache 0.0.42__tar.gz → 0.0.44__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quadkit_cache-0.0.44/.gitignore +24 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/PKG-INFO +1 -1
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/pyproject.toml +1 -7
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/admin/contributor.py +1 -1
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/redis/circuit.py +1 -1
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/redis/resilience.py +2 -2
- quadkit_cache-0.0.42/.gitignore +0 -214
- quadkit_cache-0.0.42/docs/ARCHITECTURE.md +0 -275
- quadkit_cache-0.0.42/docs/BACKENDS.md +0 -171
- quadkit_cache-0.0.42/docs/CONFIGURATION.md +0 -126
- quadkit_cache-0.0.42/docs/GUIDE.md +0 -212
- quadkit_cache-0.0.42/docs/HOWTOS.md +0 -185
- quadkit_cache-0.0.42/docs/QUICKSTART.md +0 -76
- quadkit_cache-0.0.42/docs/TROUBLESHOOTING.md +0 -148
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/LICENSE +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/NOTICE +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/README.md +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/example.yaml +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/admin/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/admin/handlers/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/admin/handlers/backend_ping.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/admin/handlers/eviction_rate.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/admin/handlers/hit_miss_ratio.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/admin/pages/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/admin/pages/keys.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/admin/pages/overview.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/admin/pages/stats.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/factory.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/hash.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/memcached/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/memcached/backend.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/memcached/py.typed +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/memory/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/memory/backend.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/memory/kv.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/memory/py.typed +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/memory_lock.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/memory_secrets.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/memory_state.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/py.typed +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/redis/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/redis/backend.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/redis/py.typed +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/redis/resilient.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/registry.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/cli/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/cli/checks.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/cli/commands.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/cli/contributor.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/cli/doctor.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/cli/generators/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/cli/generators/cache_repository.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/cli/shell.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/cli/templates/cache_repository.py.jinja2 +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/config/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/config/backends.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/config/loaders.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/config/operation.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/config/service.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/config/top_level.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/constants.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/decorators.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/di/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/di/_registration.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/di/_services.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/di/provider.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/di/py.typed +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/events.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/exceptions.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/hooks.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/locks/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/locks/auto_renewing.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/locks/context.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/locks/distributed.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/locks/manager.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/module.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/protocols.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/py.typed +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/repository/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/repository/base.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/repository/collections.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/repository/py.typed +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/semantic/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/semantic/cost_decision.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/semantic/protocols.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/semantic/store.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/semantic/vector_index.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/serialization/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/serialization/base.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/serialization/compression.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/serialization/factory.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/serialization/json.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/serialization/msgpack.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/serialization/py.typed +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/serialization/type_registry.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/service/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/service/core.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/service/decorators.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/service/factory.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/service/health.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/service/invalidation.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/service/patterns.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/service/pipeline.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/service/protection.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/service/py.typed +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/service/registry.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/service/request_cache.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/service/stampede.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/service/warmer.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/stores/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/stores/redis_lock.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/stores/redis_secrets.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/stores/redis_state.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/strategies/py.typed +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/strategies/tiered.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/strategies/write_through.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/types.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/py.typed +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/conftest.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/integration/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/integration/conftest.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/integration/test_cache_lifecycle.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/integration/test_redis_cache_compliance.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/integration/test_redis_provider_lifecycle.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/abstractions/test_cache_backend.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/admin/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/admin/handlers/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/admin/handlers/test_backend_ping.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/admin/handlers/test_eviction_rate.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/admin/handlers/test_hit_miss_ratio.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/admin/test_cache_contributor.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/backends/test_backend_error_logging.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/backends/test_backend_registry_extra.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/backends/test_memcached.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/backends/test_memcached_client_precedence.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/backends/test_memory.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/backends/test_memory_eviction.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/backends/test_memory_kv.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/backends/test_memory_lock.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/backends/test_memory_secrets.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/backends/test_memory_stats.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/backends/test_redis.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/backends/test_resilient_redis.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/cli/test_generators_contract.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/conftest.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/integration/test_backends_create.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/integration/test_config.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/integration/test_integration_helpers.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/integration/test_provider_quadkit_cache_integration.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/semantic/__init__.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/semantic/test_semantic_cache_eviction.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/semantic/test_semantic_cache_lookup.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/semantic/test_semantic_cache_store.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/semantic/test_semantic_cache_vector_index.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/serialization/test_base.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/serialization/test_serialization.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/service/test_cache_service_get_typed.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/service/test_decorators.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/service/test_invalidation.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/service/test_service.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/service/test_stampede_protection.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/stores/test_redis_lock_extra.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/stores/test_redis_secrets_extra.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/stores/test_redis_state_extra.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_admin_stats_and_events.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_admin_protocol.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_cli.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_config.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_config_loaders.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_config_top_level.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_constants.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_di_registration.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_exceptions.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_module.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_protocols.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_provider.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_provider_named.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_providers.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_result_pattern.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_stores.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_strategies.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_types.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cache_warmer.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_cli_contributor.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_compressing_serializer.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_config_loading.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_di_boundary_contract.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_distributed_lock.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_exceptions.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_exceptions_quadkit_cache.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_lock_renewal.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_locks.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_package_hooks_p2.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_redis_fake.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_redis_resilience_backends.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_repository.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_request_cache.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_round28_hardening.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_stampede_coalescing.py +0 -0
- {quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/tests/unit/test_type_registry.py +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Generated public-repository ignore rules.
|
|
2
|
+
# Private monorepo paths are intentionally not referenced here.
|
|
3
|
+
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.py[cod]
|
|
6
|
+
*.so
|
|
7
|
+
*.egg-info/
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
.env
|
|
11
|
+
.env.*
|
|
12
|
+
!.env.example
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.ruff_cache/
|
|
15
|
+
.mypy_cache/
|
|
16
|
+
.coverage
|
|
17
|
+
coverage.xml
|
|
18
|
+
htmlcov/
|
|
19
|
+
build/
|
|
20
|
+
dist/
|
|
21
|
+
*.log
|
|
22
|
+
.DS_Store
|
|
23
|
+
.idea/
|
|
24
|
+
.vscode/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: quadkit-cache
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.44
|
|
4
4
|
Summary: Multi-backend caching system for Quadkit Framework - Redis, Memcached, and in-memory caching
|
|
5
5
|
Project-URL: Homepage, https://dbtinoy-.github.io/quadkit
|
|
6
6
|
Project-URL: Repository, https://github.com/dbtinoy-/quadkit
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "quadkit-cache"
|
|
9
|
-
version = "0.0.
|
|
9
|
+
version = "0.0.44"
|
|
10
10
|
description = "Multi-backend caching system for Quadkit Framework - Redis, Memcached, and in-memory caching"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
requires-python = ">=3.11"
|
|
@@ -95,9 +95,6 @@ Documentation = "https://dbtinoy-.github.io/quadkit"
|
|
|
95
95
|
Issues = "https://github.com/dbtinoy-/quadkit/issues"
|
|
96
96
|
Changelog = "https://github.com/dbtinoy-/quadkit/blob/main/CHANGELOG.md"
|
|
97
97
|
|
|
98
|
-
[project.entry-points."quadkit.admin.contributors"]
|
|
99
|
-
cache = "quadkit.cache.admin.contributor:CacheAdminContributor"
|
|
100
|
-
|
|
101
98
|
[project.entry-points."quadkit.cli.contributors"]
|
|
102
99
|
cache = "quadkit.cache.cli.contributor:CacheCliContributor"
|
|
103
100
|
|
|
@@ -180,9 +177,6 @@ overrides = [
|
|
|
180
177
|
[tool.uv.sources.quadkit]
|
|
181
178
|
workspace = true
|
|
182
179
|
|
|
183
|
-
[tool.uv.sources.quadkit-admin]
|
|
184
|
-
workspace = true
|
|
185
|
-
|
|
186
180
|
[tool.uv.sources.quadkit-testing]
|
|
187
181
|
workspace = true
|
|
188
182
|
|
|
@@ -138,7 +138,7 @@ _ACTIONS: tuple[AdminActionDefinition, ...] = (
|
|
|
138
138
|
class CacheAdminContributor(BaseAdminContributor):
|
|
139
139
|
"""Admin contributor for the quadkit-cache package.
|
|
140
140
|
|
|
141
|
-
Registered via ``
|
|
141
|
+
Registered via ``the optional provider module`` entry point.
|
|
142
142
|
Provides cache hit/miss ratio, eviction rate, and backend ping widgets.
|
|
143
143
|
Dependencies are resolved from the container in ``on_admin_boot``.
|
|
144
144
|
"""
|
{quadkit_cache-0.0.42 → quadkit_cache-0.0.44}/src/quadkit/cache/backends/redis/resilience.py
RENAMED
|
@@ -6,7 +6,7 @@ breakers) that require distributed, cross-process state.
|
|
|
6
6
|
Usage::
|
|
7
7
|
|
|
8
8
|
from quadkit.cache.backends.redis.resilience import RedisCircuitBreakerBackend
|
|
9
|
-
from
|
|
9
|
+
from the optional provider module import CircuitBreakerRegistry
|
|
10
10
|
|
|
11
11
|
backend = RedisCircuitBreakerBackend(
|
|
12
12
|
redis_url="redis://localhost:6379",
|
|
@@ -31,7 +31,7 @@ class RedisCircuitBreakerBackend:
|
|
|
31
31
|
|
|
32
32
|
Suitable for multi-process deployments where circuit breaker state must
|
|
33
33
|
be shared across containers or workers. Implements
|
|
34
|
-
:class:`~
|
|
34
|
+
:class:`~the optional provider module`.
|
|
35
35
|
|
|
36
36
|
Args:
|
|
37
37
|
redis_url: Redis connection URL.
|
quadkit_cache-0.0.42/.gitignore
DELETED
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
*
|
|
2
|
-
!tests/
|
|
3
|
-
!tests/**
|
|
4
|
-
!README.md
|
|
5
|
-
!AGENTS.md
|
|
6
|
-
!DEVELOPMENT.md
|
|
7
|
-
!QUICKGUIDE.md
|
|
8
|
-
!MILESTONE*.md
|
|
9
|
-
!TESTS*.md
|
|
10
|
-
!CHANGELOG.md
|
|
11
|
-
!AUDIT*.md
|
|
12
|
-
!REF*.md
|
|
13
|
-
!AUDIT*.json
|
|
14
|
-
!LICENSE
|
|
15
|
-
!LICENSE-MIT.txt
|
|
16
|
-
!NOTICE
|
|
17
|
-
!TRADEMARK.md
|
|
18
|
-
!conftest.py
|
|
19
|
-
!CONTRIBUTING.md
|
|
20
|
-
!CODE_OF_CONDUCT.md
|
|
21
|
-
!SECURITY.md
|
|
22
|
-
!GOVERNANCE.md
|
|
23
|
-
!pyproject.toml
|
|
24
|
-
!uv.lock
|
|
25
|
-
!docker-compose.yml
|
|
26
|
-
!.env.example
|
|
27
|
-
!.env.full.example
|
|
28
|
-
!.importlinter
|
|
29
|
-
!.superpowers/
|
|
30
|
-
!.superpowers/**
|
|
31
|
-
!.gitattributes
|
|
32
|
-
!.editorconfig
|
|
33
|
-
!Makefile
|
|
34
|
-
!docker-compose.test.yml
|
|
35
|
-
!quadkit.yaml.example
|
|
36
|
-
!.github/
|
|
37
|
-
!.github/workflows/
|
|
38
|
-
!.github/workflows/**
|
|
39
|
-
!.github/dependabot.yml
|
|
40
|
-
!.devcontainer/
|
|
41
|
-
!.devcontainer/**
|
|
42
|
-
!tools/
|
|
43
|
-
!tools/**
|
|
44
|
-
!*example.yaml
|
|
45
|
-
|
|
46
|
-
# Quadkit Packages
|
|
47
|
-
!quadkit*/
|
|
48
|
-
!quadkit*/pyproject.toml
|
|
49
|
-
!quadkit*/CHANGELOG.md
|
|
50
|
-
!quadkit*/README.md
|
|
51
|
-
!quadkit*/alembic.ini
|
|
52
|
-
!quadkit*/mypy.ini
|
|
53
|
-
!quadkit*/*.yaml.example
|
|
54
|
-
!quadkit*/src/
|
|
55
|
-
!quadkit*/src/**
|
|
56
|
-
!quadkit*/docs/
|
|
57
|
-
!quadkit*/docs/**
|
|
58
|
-
!quadkit*/tests/
|
|
59
|
-
!quadkit*/tests/**
|
|
60
|
-
|
|
61
|
-
# Keep quadkit docs
|
|
62
|
-
!/docs/
|
|
63
|
-
!docs/**
|
|
64
|
-
|
|
65
|
-
# Session / cutover plans at the repo root
|
|
66
|
-
!plans/
|
|
67
|
-
!plans/**
|
|
68
|
-
|
|
69
|
-
# Keep example files for README examples
|
|
70
|
-
!/examples/
|
|
71
|
-
!/examples/**
|
|
72
|
-
|
|
73
|
-
# Re-ignore these AFTER negations (last match wins)
|
|
74
|
-
*.tape
|
|
75
|
-
examples/*/runs/
|
|
76
|
-
__pycache__/
|
|
77
|
-
*.pyc
|
|
78
|
-
*.pyo
|
|
79
|
-
*.egg-info/
|
|
80
|
-
dist/
|
|
81
|
-
build/
|
|
82
|
-
.eggs/
|
|
83
|
-
*.egg
|
|
84
|
-
.pytest_cache/
|
|
85
|
-
.mypy_cache/
|
|
86
|
-
.ruff_cache/
|
|
87
|
-
htmlcov/
|
|
88
|
-
.coverage
|
|
89
|
-
coverage.xml
|
|
90
|
-
test.db
|
|
91
|
-
!quadkit*/tests/fixtures/test.db
|
|
92
|
-
*.log
|
|
93
|
-
*.tmp
|
|
94
|
-
|
|
95
|
-
# Company-specific files (keep private, do not commit)
|
|
96
|
-
.company/
|
|
97
|
-
COMPANY_*.md
|
|
98
|
-
company-config/
|
|
99
|
-
internal-docs/
|
|
100
|
-
# Editor swap files
|
|
101
|
-
*.swp
|
|
102
|
-
|
|
103
|
-
!core/
|
|
104
|
-
!packages/
|
|
105
|
-
!core/quadkit*/
|
|
106
|
-
!packages/quadkit*/
|
|
107
|
-
!core/quadkit*/pyproject.toml
|
|
108
|
-
!packages/quadkit*/pyproject.toml
|
|
109
|
-
!core/quadkit*/CHANGELOG.md
|
|
110
|
-
!packages/quadkit*/CHANGELOG.md
|
|
111
|
-
!core/quadkit*/README.md
|
|
112
|
-
!packages/quadkit*/README.md
|
|
113
|
-
!core/quadkit*/alembic.ini
|
|
114
|
-
!packages/quadkit*/alembic.ini
|
|
115
|
-
!core/quadkit*/mypy.ini
|
|
116
|
-
!packages/quadkit*/mypy.ini
|
|
117
|
-
!core/quadkit*/*.yaml.example
|
|
118
|
-
!packages/quadkit*/*.yaml.example
|
|
119
|
-
!core/quadkit*/src/
|
|
120
|
-
!core/quadkit*/src/**
|
|
121
|
-
!packages/quadkit*/src/
|
|
122
|
-
!packages/quadkit*/src/**
|
|
123
|
-
!core/quadkit*/tests/
|
|
124
|
-
!core/quadkit*/tests/**
|
|
125
|
-
!packages/quadkit*/tests/
|
|
126
|
-
!packages/quadkit*/tests/**
|
|
127
|
-
!core/quadkit*/docs/
|
|
128
|
-
!core/quadkit*/docs/**
|
|
129
|
-
!packages/quadkit*/docs/
|
|
130
|
-
!packages/quadkit*/docs/**
|
|
131
|
-
|
|
132
|
-
# dev/
|
|
133
|
-
!dev/
|
|
134
|
-
!dev/**
|
|
135
|
-
|
|
136
|
-
# Admin dev playground (committed dev asset; local SQLite files stay ignored)
|
|
137
|
-
!packages/quadkit-admin/playground/
|
|
138
|
-
!packages/quadkit-admin/playground/serve.py
|
|
139
|
-
|
|
140
|
-
# gatewai apps
|
|
141
|
-
!apps/
|
|
142
|
-
!apps/**
|
|
143
|
-
!apps/gatewai/
|
|
144
|
-
!apps/gatewai/**
|
|
145
|
-
apps/**/node_modules/
|
|
146
|
-
apps/**/dist/
|
|
147
|
-
apps/**/__pycache__/
|
|
148
|
-
apps/**/*.pyc
|
|
149
|
-
apps/**/*.db
|
|
150
|
-
apps/**/*.db-wal
|
|
151
|
-
apps/**/*.db-shm
|
|
152
|
-
apps/**/.env
|
|
153
|
-
apps/**/.env.*
|
|
154
|
-
!apps/**/.env.example
|
|
155
|
-
!apps/**/.env.full.example
|
|
156
|
-
|
|
157
|
-
# Piccolina — nested app tracked in this monorepo (standalone .git removed).
|
|
158
|
-
# Keep marketing packages and deployment secrets out of the tree.
|
|
159
|
-
apps/piccolina/piccolina-marketing/
|
|
160
|
-
apps/piccolina/piccolina-marketing-plans/
|
|
161
|
-
apps/piccolina/secrets/
|
|
162
|
-
apps/piccolina/**/secrets/
|
|
163
|
-
apps/piccolina/**/*.pem
|
|
164
|
-
apps/piccolina/**/*.key
|
|
165
|
-
apps/piccolina/**/.venv/
|
|
166
|
-
apps/piccolina/**/node_modules/
|
|
167
|
-
apps/piccolina/**/__pycache__/
|
|
168
|
-
apps/piccolina/**/*.pyc
|
|
169
|
-
apps/piccolina/**/*.pbf
|
|
170
|
-
apps/piccolina/**/.open-next/
|
|
171
|
-
apps/piccolina/**/storage/
|
|
172
|
-
apps/piccolina/**/.deploy-host
|
|
173
|
-
|
|
174
|
-
# Re-exclude private content under the tiered paths.
|
|
175
|
-
# MUST come after the !...docs/** negations above — last match wins.
|
|
176
|
-
core/quadkit*/docs/plans/
|
|
177
|
-
packages/quadkit*/docs/plans/
|
|
178
|
-
core/quadkit*/docs/**/
|
|
179
|
-
packages/quadkit*/docs/**/
|
|
180
|
-
|
|
181
|
-
# Re-exclude tooling artifacts under the tiered paths
|
|
182
|
-
__pycache__/
|
|
183
|
-
*.pyc
|
|
184
|
-
*.pyo
|
|
185
|
-
*.egg-info/
|
|
186
|
-
.pytest_cache/
|
|
187
|
-
.mypy_cache/
|
|
188
|
-
.ruff_cache/
|
|
189
|
-
htmlcov/
|
|
190
|
-
.coverage
|
|
191
|
-
coverage.xml
|
|
192
|
-
test.db
|
|
193
|
-
*.tmp
|
|
194
|
-
*.log
|
|
195
|
-
!core/quadkit*/tests/fixtures/test.db
|
|
196
|
-
!packages/quadkit*/tests/fixtures/test.db
|
|
197
|
-
|
|
198
|
-
# Allow the generated example gifs to be tracked (README hero images).
|
|
199
|
-
!core/quadkit*/docs/gifs/
|
|
200
|
-
!core/quadkit*/docs/gifs/**
|
|
201
|
-
!packages/*/quadkit*/docs/gifs/
|
|
202
|
-
!packages/*/quadkit*/docs/gifs/**
|
|
203
|
-
examples/*/screenshot.png
|
|
204
|
-
!banner*
|
|
205
|
-
apps/quadkit-playground/**/node_modules/
|
|
206
|
-
apps/quadkit-playground/**/dist/
|
|
207
|
-
apps/quadkit-playground/**/__pycache__/
|
|
208
|
-
apps/quadkit-playground/**/*.pyc
|
|
209
|
-
apps/quadkit-playground/**/*.db
|
|
210
|
-
apps/quadkit-playground/**/*.db-wal
|
|
211
|
-
apps/quadkit-playground/**/*.db-shm
|
|
212
|
-
apps/quadkit-playground/projects/
|
|
213
|
-
apps/quadkit-playground/quadkit/
|
|
214
|
-
docs/superpowers/
|
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
# Architecture
|
|
2
|
-
|
|
3
|
-
Internal design of the `quadkit-cache` package.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Role in the System
|
|
8
|
-
|
|
9
|
-
`quadkit-cache` is the **caching abstraction layer** in the Quadkit framework. It depends only on `quadkit` and `quadkit-contracts`. Other packages consume caching through `CacheBackendProtocol` resolved via the DI container — never through direct imports.
|
|
10
|
-
|
|
11
|
-
```mermaid
|
|
12
|
-
flowchart BT
|
|
13
|
-
App[Application Layer]
|
|
14
|
-
Ext[quadkit-* Extensions]
|
|
15
|
-
LC[quadkit-cache<br/>CacheService · Backends · Stampede Protection]
|
|
16
|
-
Core[quadkit<br/>DI · Config · Logging]
|
|
17
|
-
Contracts[quadkit-contracts<br/>CacheBackendProtocol · CacheKeyBuilderProtocol]
|
|
18
|
-
|
|
19
|
-
App --> Ext
|
|
20
|
-
Ext --> LC
|
|
21
|
-
LC --> Core
|
|
22
|
-
LC --> Contracts
|
|
23
|
-
Core --> Contracts
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
**Import direction:** Arrows point toward the dependency. Services depend on protocols from contracts; backends implement those protocols. The cache provider wires both sides through the container.
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## Backend Abstraction
|
|
31
|
-
|
|
32
|
-
All cache backends implement `CacheBackendProtocol` from `quadkit-contracts`. Operations return `Result[T, CacheError]` — backend errors are signalled via `Err` rather than exceptions, letting callers decide how to handle failures.
|
|
33
|
-
|
|
34
|
-
```mermaid
|
|
35
|
-
flowchart LR
|
|
36
|
-
subgraph Contracts[quadkit-contracts]
|
|
37
|
-
CBP[CacheBackendProtocol<br/>get · set · delete · clear<br/>get_many · set_many · delete_many<br/>exists · delete_pattern · health_check]
|
|
38
|
-
end
|
|
39
|
-
subgraph Backends[quadkit-cache · backends]
|
|
40
|
-
MEM[MemoryCacheBackend<br/>in-process dict<br/>TTL · LRU eviction]
|
|
41
|
-
REDIS[RedisCacheBackend<br/>StateStoreProtocol<br/>pipeline · SCAN]
|
|
42
|
-
MEMCACHED[MemcachedCacheBackend<br/>pymemcache]
|
|
43
|
-
end
|
|
44
|
-
subgraph ThirdParty[Third Party]
|
|
45
|
-
EP[entry-point<br/>quadkit.cache.backends]
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
Backends --> CBP
|
|
49
|
-
EP --> CBP
|
|
50
|
-
CBP -->|DI binding| Provider[CacheProvider<br/>register · boot · shutdown]
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### Backend Selection
|
|
54
|
-
|
|
55
|
-
Backends are configured via `CacheConfig.backends`. Each backend specifies a `name`, `type` (memory / redis / memcached), and `default` flag. The provider registers an unnamed `CacheBackendProtocol` singleton for the default backend and named singletons for all enabled backends:
|
|
56
|
-
|
|
57
|
-
```python
|
|
58
|
-
container.singleton(CacheBackendProtocol, factory=lambda: self.get_backend(None))
|
|
59
|
-
container.singleton(CacheBackendProtocol, factory=lambda n=name: self.get_backend(n), name=name)
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## Cache Operations
|
|
65
|
-
|
|
66
|
-
`CacheService` is the main high-level API. It composes three mixins:
|
|
67
|
-
|
|
68
|
-
| Class | Source | Adds |
|
|
69
|
-
|-------|--------|------|
|
|
70
|
-
| `CacheService` | `core.py` | `get`, `set`, `delete`, `exists`, `clear`, `delete_pattern`, `get_typed`, health check |
|
|
71
|
-
| `PipelineMixin` | `pipeline.py` | `get_many`, `set_many`, `delete_many` |
|
|
72
|
-
| `InvalidationMixin` | `invalidation.py` | `set_with_tags`, `invalidate_by_tag` |
|
|
73
|
-
| `PatternsMixin` | `patterns.py` | `get_or_set`, `get_or_compute`, `get_or_set_result`, `remember` |
|
|
74
|
-
|
|
75
|
-
### Key Behaviours
|
|
76
|
-
|
|
77
|
-
- **TTL jitter:** `set()` applies ±10% random jitter to spread expiry times and reduce thundering-herd risk.
|
|
78
|
-
- **Fast path:** Primitive values (`str`, `int`, `float`, `bool`) are stored directly without serialization round-trip.
|
|
79
|
-
- **Namespace scoping:** Keys can be request-scoped via `request_scoped=True`, which prepends `req:<request_id>:` to the key.
|
|
80
|
-
- **Error handling:** Backend errors are caught (`RuntimeError`, `OSError`, `ConnectionError`, etc.) and logged; `get` returns `default`, `set`/`delete` return `False`.
|
|
81
|
-
- **Metrics:** Every operation updates an in-memory counter (`hits`, `misses`, `errors`, `operations`). Exposed via `get_metrics()` and `reset_metrics()`.
|
|
82
|
-
|
|
83
|
-
```python
|
|
84
|
-
cache = container.resolve(CacheService)
|
|
85
|
-
|
|
86
|
-
await cache.set("user:42", profile, ttl=300)
|
|
87
|
-
profile = await cache.get("user:42", default=None)
|
|
88
|
-
await cache.delete("user:42")
|
|
89
|
-
|
|
90
|
-
batch = await cache.get_many(["a", "b", "c"])
|
|
91
|
-
await cache.set_many({"x": 1, "y": 2}, ttl=60)
|
|
92
|
-
|
|
93
|
-
# Tag-based invalidation
|
|
94
|
-
await cache.set_with_tags("pet:1", pet, tags=["user:42"])
|
|
95
|
-
await cache.invalidate_by_tag("user:42")
|
|
96
|
-
|
|
97
|
-
# Type-safe access
|
|
98
|
-
count = await cache.get_typed("counter", type_=int, default=0)
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
---
|
|
102
|
-
|
|
103
|
-
## Stampede Protection
|
|
104
|
-
|
|
105
|
-
Two complementary mechanisms prevent cache stampedes (thundering-herd problems):
|
|
106
|
-
|
|
107
|
-
### 1. TTL Jitter (Built Into CacheService)
|
|
108
|
-
|
|
109
|
-
`_add_ttl_jitter()` applies ±10% random variation to every TTL, spreading out natural expiry times so multiple keys don't expire simultaneously.
|
|
110
|
-
|
|
111
|
-
### 2. Single-Flight Pattern (StampedeProtectedCache)
|
|
112
|
-
|
|
113
|
-
`StampedeProtectedCache` wraps a `CacheBackendProtocol` (injected via `@inject`) and implements single-flight coalescing:
|
|
114
|
-
|
|
115
|
-
```mermaid
|
|
116
|
-
flowchart TD
|
|
117
|
-
R1[Request A<br/>get_or_compute(key)]
|
|
118
|
-
R2[Request B<br/>get_or_compute(key)]
|
|
119
|
-
R3[Request C<br/>get_or_compute(key)]
|
|
120
|
-
STAMP[StampedeProtectedCache]
|
|
121
|
-
CACHE[Cache Backend]
|
|
122
|
-
COMPUTE[Compute Function]
|
|
123
|
-
|
|
124
|
-
R1 --> STAMP
|
|
125
|
-
R2 --> STAMP
|
|
126
|
-
R3 --> STAMP
|
|
127
|
-
|
|
128
|
-
STAMP -->|try cache| CACHE
|
|
129
|
-
CACHE -->|miss| STAMP
|
|
130
|
-
STAMP -->|acquire lock| STAMP
|
|
131
|
-
STAMP -->|double-check cache| CACHE
|
|
132
|
-
CACHE -->|still miss| STAMP
|
|
133
|
-
STAMP -->|single flight task| COMPUTE
|
|
134
|
-
COMPUTE -->|value| STAMP
|
|
135
|
-
STAMP -->|store + return| R1
|
|
136
|
-
STAMP -->|return same| R2
|
|
137
|
-
STAMP -->|return same| R3
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
Key implementation details:
|
|
141
|
-
|
|
142
|
-
| Mechanism | How It Works |
|
|
143
|
-
|-----------|--------------|
|
|
144
|
-
| **In-process lock** | `asyncio.Lock` per key via `WeakValueDictionary` — concurrent requests for the same key serialize |
|
|
145
|
-
| **Double-check** | After acquiring the lock, re-checks cache (another request may have filled it) |
|
|
146
|
-
| **Single-flight task** | One `asyncio.Task` per key; subsequent requests `await` the same task |
|
|
147
|
-
| **XFetch early refresh** | Probabilistic early refresh: the closer to expiry, the higher the probability a request refreshes before the key expires |
|
|
148
|
-
|
|
149
|
-
```python
|
|
150
|
-
from quadkit.cache.service.stampede import StampedeProtectedCache
|
|
151
|
-
|
|
152
|
-
protected = StampedeProtectedCache(backend, lock_timeout=10)
|
|
153
|
-
|
|
154
|
-
# Only one compute call per key, regardless of concurrent requests
|
|
155
|
-
result = await protected.get_or_compute(
|
|
156
|
-
key="expensive:data",
|
|
157
|
-
compute=load_data_from_db,
|
|
158
|
-
ttl=300,
|
|
159
|
-
ttl_jitter=0.2,
|
|
160
|
-
)
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
### XFetch Algorithm
|
|
164
|
-
|
|
165
|
-
When `ttl_jitter > 0`, `StampedeProtectedCache` applies probabilistic early refresh (XFetch). A request that finds a cached but partially-stale entry will refresh it with probability proportional to how close it is to expiry:
|
|
166
|
-
|
|
167
|
-
```
|
|
168
|
-
staleness = 1.0 - (time_left / ttl)
|
|
169
|
-
refresh if random() < staleness # higher probability near expiry
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
## Provider Lifecycle
|
|
175
|
-
|
|
176
|
-
```mermaid
|
|
177
|
-
sequenceDiagram
|
|
178
|
-
actor User as Container
|
|
179
|
-
participant P as CacheProvider
|
|
180
|
-
participant REG as BackendRegistry
|
|
181
|
-
participant B as Backend Instance
|
|
182
|
-
participant PROT as StampedeProtectedCache
|
|
183
|
-
participant SVC as CacheService
|
|
184
|
-
|
|
185
|
-
User->>P: CacheProvider(config)
|
|
186
|
-
P->>P: configure(config)
|
|
187
|
-
|
|
188
|
-
User->>P: register(container)
|
|
189
|
-
P->>REG: BackendRegistry()
|
|
190
|
-
P->>REG: register entry-point backends
|
|
191
|
-
P->>P: bind factories (no I/O)
|
|
192
|
-
Note over P: CacheBackendProtocol → lazy factory<br/>CacheService → lazy factory<br/>BackendRegistry → singleton<br/>CacheStatusRegistry → singleton
|
|
193
|
-
|
|
194
|
-
User->>P: boot(container)
|
|
195
|
-
P->>B: create_backend(config)
|
|
196
|
-
Note over B: Redis connect<br/>or Memory init
|
|
197
|
-
B-->>P: backend instance
|
|
198
|
-
P->>PROT: StampedeProtectedCache(backend)
|
|
199
|
-
P->>SVC: CacheService(provider, protection)
|
|
200
|
-
Note over SVC: PipelineMixin + InvalidationMixin<br/>+ PatternsMixin composed
|
|
201
|
-
|
|
202
|
-
User->>SVC: get/set operations
|
|
203
|
-
SVC->>B: delegated backend call
|
|
204
|
-
|
|
205
|
-
User->>P: shutdown()
|
|
206
|
-
P->>SVC: close()
|
|
207
|
-
P->>B: close()
|
|
208
|
-
Note over P: services.clear()<br/>backends.clear()
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### Provider Configuration
|
|
212
|
-
|
|
213
|
-
```python
|
|
214
|
-
class CacheProvider(Provider):
|
|
215
|
-
name = "cache"
|
|
216
|
-
priority = ProviderPriority.INFRASTRUCTURE # 10
|
|
217
|
-
config_key = "cache" # QK_CACHE__* env vars
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
| Phase | Action | I/O? |
|
|
221
|
-
|-------|--------|------|
|
|
222
|
-
| `__init__` | Store config, initialize serializers | No |
|
|
223
|
-
| `register()` | Bind lazy factories, discover entry-point backends, register admin components | No |
|
|
224
|
-
| `boot()` | Create backends (open connections), wire stampede protection, create services, register admin hooks | Yes |
|
|
225
|
-
| `shutdown()` | Close services → close backends → clear state | Yes |
|
|
226
|
-
|
|
227
|
-
---
|
|
228
|
-
|
|
229
|
-
## Contracts Used
|
|
230
|
-
|
|
231
|
-
| Contract | Source | Purpose |
|
|
232
|
-
|----------|--------|---------|
|
|
233
|
-
| `CacheBackendProtocol` | `quadkit.contracts.infra.cache` | Primary backend interface: `get`, `set`, `delete`, `clear`, `exists`, `get_many`, `set_many`, `delete_many`, `delete_pattern`, `health_check` |
|
|
234
|
-
| `CacheKeyBuilderProtocol` | `quadkit.contracts.infra.cache` | Key construction and namespace management |
|
|
235
|
-
| `CacheProtectionStrategyProtocol` | `quadkit.contracts.infra.cache` | Stampede protection contract |
|
|
236
|
-
| `CacheProviderProtocol` | `quadkit.contracts.infra.cache` | Provider self-reference for DI resolution |
|
|
237
|
-
| `CacheHealthCheckerProtocol` | `quadkit.contracts.infra.cache` | Backend health checks |
|
|
238
|
-
| `StateStoreProtocol` | `quadkit.contracts.infra` | Low-level key-value store (Redis backends) |
|
|
239
|
-
| `AsyncStringSerializerProtocol` | `quadkit.contracts.core.serialization` | Serialization interface (JSON, Pickle, Msgpack) |
|
|
240
|
-
| `SemanticCacheProtocol` | `quadkit.contracts.ai.llm` | Semantic caching (optional, requires faiss) |
|
|
241
|
-
| `EmbeddingClientProtocol` | `quadkit.contracts.ai.llm` | Embedding generation for semantic caching |
|
|
242
|
-
| `HealthCheckResult` | `quadkit.contracts.core` | Structured health check type |
|
|
243
|
-
| `HookRegistryProtocol` | `quadkit.contracts.core` | Lifecycle hook emission |
|
|
244
|
-
|
|
245
|
-
---
|
|
246
|
-
|
|
247
|
-
## Extension Points
|
|
248
|
-
|
|
249
|
-
| Point | Mechanism |
|
|
250
|
-
|-------|-----------|
|
|
251
|
-
| Custom backend | Implement `CacheBackendProtocol`, register via `quadkit.cache.backends` entry point |
|
|
252
|
-
| Custom serializer | Implement `AsyncStringSerializerProtocol` |
|
|
253
|
-
| Custom cache key builder | Implement `CacheKeyBuilderProtocol` |
|
|
254
|
-
| Custom stampede strategy | Implement `CacheProtectionStrategyProtocol` |
|
|
255
|
-
| Named multi-backend | Add entries to `CacheConfig.backends` with unique `name` |
|
|
256
|
-
| Cache status handler | Implement `CacheStatusHandler`, register with `CacheStatusRegistry` |
|
|
257
|
-
| Cache warmer | Subclass or configure `CacheWarmer` |
|
|
258
|
-
| Semantic caching | Install `faiss`, provide `EmbeddingClientProtocol` |
|
|
259
|
-
| Cacheable decorator | Use `@cacheable` from `decorators.py` |
|
|
260
|
-
| Repository caching | Use `CacheRepository` from `repository/` with invalidation observer pattern |
|
|
261
|
-
| Admin widgets | Registered via `CacheAdminContributor`; extend with custom handlers |
|
|
262
|
-
| Operator CLI | Extend the `quadkit cache` CLI commands |
|
|
263
|
-
| Hooks | Register `HookRegistryProtocol` action handlers (`cache.hit`, `cache.miss`, `cache.evicted`) |
|
|
264
|
-
|
|
265
|
-
### Backend Registration via Entry Points
|
|
266
|
-
|
|
267
|
-
Third-party backends self-register using the `quadkit.cache.backends` entry point group. During `register()` the provider scans this group and calls each provider's `register()` method:
|
|
268
|
-
|
|
269
|
-
```python
|
|
270
|
-
# pyproject.toml
|
|
271
|
-
[project.entry-points."quadkit.cache.backends"]
|
|
272
|
-
my_backend = "my_package:MyBackendProvider"
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
Each entry point must resolve to a `Provider` subclass. The provider handles container bindings for its backend; the CacheProvider's `boot()` never references third-party types directly.
|