GeneralManager 0.48.0__tar.gz → 0.50.0__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.
- {generalmanager-0.48.0/src/GeneralManager.egg-info → generalmanager-0.50.0}/PKG-INFO +1 -1
- {generalmanager-0.48.0 → generalmanager-0.50.0}/pyproject.toml +1 -1
- {generalmanager-0.48.0 → generalmanager-0.50.0/src/GeneralManager.egg-info}/PKG-INFO +1 -1
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/property.py +1 -1
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/cache_decorator.py +105 -62
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/dependency_cache.py +10 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/dependency_index.py +17 -1
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/dependency_publish.py +91 -1
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/run_context.py +98 -6
- {generalmanager-0.48.0 → generalmanager-0.50.0}/LICENSE +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/README.md +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/setup.cfg +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/GeneralManager.egg-info/SOURCES.txt +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/GeneralManager.egg-info/requires.txt +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/GeneralManager.egg-info/top_level.txt +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/api.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/bucket.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/cache.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/factory.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/general_manager.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/interface.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/manager.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/measurement.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/permission.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/rule.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/search.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/_types/utils.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql_errors.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql_mutations.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql_prefetch.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql_resolvers.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql_search.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql_subscriptions.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/graphql_view.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/mutation.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/registry.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/remote_api.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/remote_invalidation.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/api/remote_invalidation_client.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/apps.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/bootstrap.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/bucket/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/bucket/base_bucket.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/bucket/calculation_bucket.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/bucket/database_bucket.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/bucket/group_bucket.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/bucket/request_bucket.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/cache_tracker.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/model_dependency_collector.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/signals.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/conf.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/factory/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/factory/auto_factory.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/factory/factories.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/factory/factory_methods.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/base_interface.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/bundles/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/bundles/calculation.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/bundles/database.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/bundles/remote_manager.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/bundles/request.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/base.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/builtin.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/configuration.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/core/observability.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/core/utils.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/exceptions.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/factory.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm/history.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm/mutations.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm/support.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/registry.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/infrastructure/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/interfaces/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/interfaces/calculation.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/interfaces/database.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/interfaces/existing_model.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/interfaces/read_only.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/interfaces/request.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/manifests/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/manifests/capability_builder.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/manifests/capability_models.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/orm_interface.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/requests.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/utils/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/utils/errors.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/interface/utils/models.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/logging.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/management/commands/search_index.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/management/commands/shell.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/manager/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/manager/general_manager.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/manager/group_manager.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/manager/input.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/manager/meta.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/measurement/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/measurement/measurement.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/measurement/measurement_field.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/metrics/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/metrics/graphql.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/migrations/0001_initial.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/migrations/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/models.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/audit.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/base_permission.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/graphql_capabilities.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/manager_based_permission.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/mutation_permission.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/permission_checks.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/permission_data_manager.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/permission/utils.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/public_api_registry.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/py.typed +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/rule/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/rule/handler.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/rule/rule.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/async_tasks.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/backend.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/backend_registry.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/backends/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/backends/dev.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/backends/meilisearch.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/backends/opensearch.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/backends/typesense.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/config.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/indexer.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/registry.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/search/utils.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/seeding/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/seeding/manager_landscape.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/args_to_kwargs.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/filter_parser.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/format_string.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/json_encoder.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/make_cache_key.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/none_to_zero.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/path_mapping.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/public_api.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/testing.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/utils/type_checks.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/actions.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/backend_registry.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/backends/__init__.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/backends/celery.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/backends/local.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/backends/n8n.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/config.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/engine.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/event_registry.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/events.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/models.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/signal_bridge.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/tasks.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/workflow/telemetry.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/tests/test_settings.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/tests/test_urls.py +0 -0
- {generalmanager-0.48.0 → generalmanager-0.50.0}/tests/testing_asgi.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GeneralManager
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.50.0
|
|
4
4
|
Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
|
|
5
5
|
Author-email: Tim Kleindick <tkleindick@yahoo.de>
|
|
6
6
|
License: MIT License
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "GeneralManager"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.50.0"
|
|
8
8
|
description = "Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "Tim Kleindick", email = "tkleindick@yahoo.de" }]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GeneralManager
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.50.0
|
|
4
4
|
Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
|
|
5
5
|
Author-email: Tim Kleindick <tkleindick@yahoo.de>
|
|
6
6
|
License: MIT License
|
|
@@ -95,7 +95,7 @@ class GraphQLProperty(property):
|
|
|
95
95
|
selected_cache = self.cache
|
|
96
96
|
if selected_cache == "none":
|
|
97
97
|
return self._raw_fget
|
|
98
|
-
return cached(
|
|
98
|
+
return cached(cache=cast(Literal["dependency", "run"], selected_cache))(
|
|
99
99
|
self._raw_fget
|
|
100
100
|
)
|
|
101
101
|
|
{generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/cache_decorator.py
RENAMED
|
@@ -11,6 +11,7 @@ from typing import (
|
|
|
11
11
|
Set,
|
|
12
12
|
TypeVar,
|
|
13
13
|
cast,
|
|
14
|
+
overload,
|
|
14
15
|
)
|
|
15
16
|
|
|
16
17
|
from django.core.cache import cache as django_cache
|
|
@@ -28,6 +29,7 @@ from general_manager.cache.dependency_index import (
|
|
|
28
29
|
)
|
|
29
30
|
from general_manager.cache.dependency_publish import (
|
|
30
31
|
CachePublishAborted,
|
|
32
|
+
PendingDependencyCachePublication,
|
|
31
33
|
acquire_compute_lease,
|
|
32
34
|
publish_dependency_cache_entry,
|
|
33
35
|
release_compute_lease,
|
|
@@ -87,102 +89,122 @@ class UnsupportedCacheScopeError(ValueError):
|
|
|
87
89
|
|
|
88
90
|
|
|
89
91
|
class CacheTimeoutConfigurationError(ValueError):
|
|
90
|
-
"""Raised when timeout is used with an incompatible cache
|
|
92
|
+
"""Raised when timeout is used with an incompatible cache setting."""
|
|
91
93
|
|
|
92
94
|
@classmethod
|
|
93
95
|
def missing_timeout(cls) -> "CacheTimeoutConfigurationError":
|
|
94
|
-
return cls('
|
|
96
|
+
return cls('cache="timeout" requires timeout')
|
|
95
97
|
|
|
96
98
|
@classmethod
|
|
97
99
|
def unexpected_timeout(cls) -> "CacheTimeoutConfigurationError":
|
|
98
|
-
return cls('timeout is only supported with
|
|
100
|
+
return cls('timeout is only supported with cache="timeout"')
|
|
99
101
|
|
|
100
102
|
|
|
103
|
+
@overload
|
|
104
|
+
def cached(func: FuncT) -> FuncT: ...
|
|
105
|
+
@overload
|
|
101
106
|
def cached(
|
|
107
|
+
func: None = None,
|
|
102
108
|
timeout: Optional[int] = None,
|
|
103
109
|
cache_backend: CacheBackend = django_cache,
|
|
104
110
|
record_fn: RecordFn = record_dependencies,
|
|
105
111
|
*,
|
|
106
|
-
|
|
107
|
-
) -> Callable[[FuncT], FuncT]:
|
|
112
|
+
cache: CacheScope = "run",
|
|
113
|
+
) -> Callable[[FuncT], FuncT]: ...
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def cached(
|
|
117
|
+
func: FuncT | None = None,
|
|
118
|
+
timeout: Optional[int] = None,
|
|
119
|
+
cache_backend: CacheBackend = django_cache,
|
|
120
|
+
record_fn: RecordFn = record_dependencies,
|
|
121
|
+
*,
|
|
122
|
+
cache: CacheScope = "run",
|
|
123
|
+
) -> FuncT | Callable[[FuncT], FuncT]:
|
|
108
124
|
"""
|
|
109
|
-
Decorator
|
|
125
|
+
Decorator for caching a function call.
|
|
110
126
|
|
|
111
127
|
By default, cached values are scoped to the active
|
|
112
128
|
:class:`~general_manager.cache.run_context.CalculationRunContext` and are
|
|
113
|
-
discarded when that run ends. Use ``
|
|
129
|
+
discarded when that run ends. Use ``cache="dependency"`` to persist values
|
|
114
130
|
in ``cache_backend`` and use ``record_fn`` to persist dependency metadata
|
|
115
|
-
for invalidation. Use ``
|
|
131
|
+
for invalidation. Use ``cache="timeout"`` with ``timeout`` set for
|
|
116
132
|
cache-backend storage with time-based expiry; dependency recording is
|
|
117
|
-
ignored for timeout-
|
|
133
|
+
ignored for timeout-cached values.
|
|
118
134
|
|
|
119
135
|
Parameters:
|
|
120
|
-
|
|
121
|
-
|
|
136
|
+
func (Callable[..., object] | None): Function being decorated when used
|
|
137
|
+
as ``@cached``. Leave unset when using ``@cached(...)``.
|
|
138
|
+
timeout (int | None): Expiration in seconds for timeout-cached values.
|
|
139
|
+
Required when ``cache`` is ``"timeout"`` and invalid with any other
|
|
122
140
|
``CacheScope``.
|
|
123
141
|
cache_backend (CacheBackend): Backend used to read and write cached results.
|
|
124
142
|
record_fn (RecordFn): Callback invoked to persist dependency metadata when
|
|
125
|
-
``
|
|
143
|
+
``cache`` is ``"dependency"``. Defaults to
|
|
126
144
|
:func:`~general_manager.cache.dependency_index.record_dependencies`.
|
|
127
|
-
|
|
145
|
+
cache (CacheScope): Cache storage strategy. ``"run"`` memoizes for the active run,
|
|
128
146
|
``"dependency"`` stores in ``cache_backend`` with dependency tracking,
|
|
129
147
|
``"timeout"`` stores in ``cache_backend`` with time-based expiry, and
|
|
130
148
|
``"none"`` disables caching.
|
|
131
149
|
|
|
132
150
|
Returns:
|
|
133
|
-
Callable:
|
|
151
|
+
Callable: Decorated function or decorator that wraps the target function
|
|
152
|
+
with caching behaviour.
|
|
134
153
|
|
|
135
154
|
Raises:
|
|
136
|
-
ValueError: Raised for invalid ``
|
|
155
|
+
ValueError: Raised for invalid ``cache``/``timeout`` combinations, including
|
|
137
156
|
unsupported ``CacheScope`` values, missing ``timeout`` for
|
|
138
|
-
``
|
|
157
|
+
``cache="timeout"``, and ``timeout`` supplied for non-timeout cache modes.
|
|
139
158
|
DependencyLockTimeoutError: Propagated from ``record_fn`` (i.e.
|
|
140
159
|
:func:`~general_manager.cache.dependency_index.record_dependencies`) when the
|
|
141
|
-
dependency-index lock cannot be acquired within the configured timeout.
|
|
160
|
+
dependency-index lock cannot be acquired within the configured timeout. The cached
|
|
142
161
|
value has already been stored at that point; only the dependency metadata is lost.
|
|
143
162
|
"""
|
|
144
|
-
if
|
|
145
|
-
raise UnsupportedCacheScopeError(
|
|
146
|
-
if
|
|
163
|
+
if cache not in {"dependency", "run", "timeout", "none"}:
|
|
164
|
+
raise UnsupportedCacheScopeError(cache)
|
|
165
|
+
if cache == "timeout" and timeout is None:
|
|
147
166
|
raise CacheTimeoutConfigurationError.missing_timeout()
|
|
148
|
-
if timeout is not None and
|
|
167
|
+
if timeout is not None and cache != "timeout":
|
|
149
168
|
raise CacheTimeoutConfigurationError.unexpected_timeout()
|
|
150
169
|
|
|
151
|
-
def decorator(
|
|
152
|
-
@wraps(
|
|
170
|
+
def decorator(decorated_func: FuncT) -> FuncT:
|
|
171
|
+
@wraps(decorated_func)
|
|
153
172
|
def wrapper(*args: object, **kwargs: object) -> object:
|
|
154
|
-
if
|
|
155
|
-
return
|
|
173
|
+
if cache == "none":
|
|
174
|
+
return decorated_func(*args, **kwargs)
|
|
156
175
|
|
|
157
|
-
if
|
|
158
|
-
key = make_cache_key(
|
|
176
|
+
if cache == "run":
|
|
177
|
+
key = make_cache_key(decorated_func, args, kwargs)
|
|
159
178
|
with ensure_calculation_run_context() as context:
|
|
160
|
-
return context.get_or_set(
|
|
179
|
+
return context.get_or_set(
|
|
180
|
+
key,
|
|
181
|
+
lambda: decorated_func(*args, **kwargs),
|
|
182
|
+
)
|
|
161
183
|
|
|
162
|
-
key = make_cache_key(
|
|
184
|
+
key = make_cache_key(decorated_func, args, kwargs)
|
|
163
185
|
|
|
164
|
-
if
|
|
186
|
+
if cache == "timeout":
|
|
165
187
|
cached_result = cache_backend.get(key, _SENTINEL)
|
|
166
188
|
if cached_result is not _SENTINEL:
|
|
167
189
|
logger.debug(
|
|
168
190
|
"cache hit",
|
|
169
191
|
context={
|
|
170
|
-
"function":
|
|
192
|
+
"function": decorated_func.__qualname__,
|
|
171
193
|
"key": key,
|
|
172
|
-
"
|
|
194
|
+
"cache": cache,
|
|
173
195
|
},
|
|
174
196
|
)
|
|
175
197
|
return cached_result
|
|
176
198
|
|
|
177
|
-
result =
|
|
199
|
+
result = decorated_func(*args, **kwargs)
|
|
178
200
|
cache_backend.set(key, result, timeout)
|
|
179
201
|
logger.debug(
|
|
180
202
|
"cache miss stored",
|
|
181
203
|
context={
|
|
182
|
-
"function":
|
|
204
|
+
"function": decorated_func.__qualname__,
|
|
183
205
|
"key": key,
|
|
184
206
|
"timeout": timeout,
|
|
185
|
-
"
|
|
207
|
+
"cache": cache,
|
|
186
208
|
},
|
|
187
209
|
)
|
|
188
210
|
return result
|
|
@@ -192,9 +214,9 @@ def cached(
|
|
|
192
214
|
logger.debug(
|
|
193
215
|
message,
|
|
194
216
|
context={
|
|
195
|
-
"function":
|
|
217
|
+
"function": decorated_func.__qualname__,
|
|
196
218
|
"key": key,
|
|
197
|
-
"
|
|
219
|
+
"cache": cache,
|
|
198
220
|
},
|
|
199
221
|
)
|
|
200
222
|
return hit.value
|
|
@@ -232,6 +254,7 @@ def cached(
|
|
|
232
254
|
)
|
|
233
255
|
lease = acquire_compute_lease(key)
|
|
234
256
|
|
|
257
|
+
lease_transferred_to_context = False
|
|
235
258
|
try:
|
|
236
259
|
cached_hit = read_dependency_cache_hit(
|
|
237
260
|
cache_backend,
|
|
@@ -243,7 +266,7 @@ def cached(
|
|
|
243
266
|
|
|
244
267
|
started_generation = get_dependency_generation()
|
|
245
268
|
with DependencyTracker() as dependencies:
|
|
246
|
-
result =
|
|
269
|
+
result = decorated_func(*args, **kwargs)
|
|
247
270
|
ModelDependencyCollector.add_args(dependencies, args, kwargs)
|
|
248
271
|
|
|
249
272
|
def record_many(
|
|
@@ -252,34 +275,52 @@ def cached(
|
|
|
252
275
|
for entry_key, entry_dependencies in entries:
|
|
253
276
|
record_fn(entry_key, set(entry_dependencies))
|
|
254
277
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
except CachePublishAborted:
|
|
268
|
-
logger.debug(
|
|
269
|
-
"dependency cache publish aborted",
|
|
270
|
-
context={
|
|
271
|
-
"function": func.__qualname__,
|
|
272
|
-
"key": key,
|
|
273
|
-
"scope": scope,
|
|
274
|
-
},
|
|
278
|
+
publish_context = current_calculation_run_context()
|
|
279
|
+
if publish_context is not None and record_fn is record_dependencies:
|
|
280
|
+
publish_context.buffer_dependency_cache_publication(
|
|
281
|
+
PendingDependencyCachePublication(
|
|
282
|
+
cache_key=key,
|
|
283
|
+
result=result,
|
|
284
|
+
dependencies=frozenset(dependencies),
|
|
285
|
+
cache_backend=cache_backend,
|
|
286
|
+
timeout=timeout,
|
|
287
|
+
started_generation=started_generation,
|
|
288
|
+
lease=lease,
|
|
289
|
+
)
|
|
275
290
|
)
|
|
291
|
+
lease_transferred_to_context = True
|
|
292
|
+
else:
|
|
293
|
+
try:
|
|
294
|
+
publish_dependency_cache_entry(
|
|
295
|
+
cache_key=key,
|
|
296
|
+
result=result,
|
|
297
|
+
dependencies=dependencies,
|
|
298
|
+
cache_backend=cache_backend,
|
|
299
|
+
timeout=timeout,
|
|
300
|
+
started_generation=started_generation,
|
|
301
|
+
record_many_fn=(
|
|
302
|
+
None
|
|
303
|
+
if record_fn is record_dependencies
|
|
304
|
+
else record_many
|
|
305
|
+
),
|
|
306
|
+
)
|
|
307
|
+
except CachePublishAborted:
|
|
308
|
+
logger.debug(
|
|
309
|
+
"dependency cache publish aborted",
|
|
310
|
+
context={
|
|
311
|
+
"function": decorated_func.__qualname__,
|
|
312
|
+
"key": key,
|
|
313
|
+
"cache": cache,
|
|
314
|
+
},
|
|
315
|
+
)
|
|
276
316
|
finally:
|
|
277
|
-
|
|
317
|
+
if not lease_transferred_to_context:
|
|
318
|
+
release_compute_lease(lease)
|
|
278
319
|
|
|
279
320
|
logger.debug(
|
|
280
321
|
"cache miss recorded",
|
|
281
322
|
context={
|
|
282
|
-
"function":
|
|
323
|
+
"function": decorated_func.__qualname__,
|
|
283
324
|
"key": key,
|
|
284
325
|
"dependency_count": len(dependencies),
|
|
285
326
|
"timeout": timeout,
|
|
@@ -288,8 +329,10 @@ def cached(
|
|
|
288
329
|
return result
|
|
289
330
|
|
|
290
331
|
# fix for python 3.14:
|
|
291
|
-
wrapper.__annotations__ =
|
|
332
|
+
wrapper.__annotations__ = decorated_func.__annotations__
|
|
292
333
|
|
|
293
334
|
return cast(FuncT, wrapper)
|
|
294
335
|
|
|
295
|
-
|
|
336
|
+
if func is None:
|
|
337
|
+
return decorator
|
|
338
|
+
return decorator(func)
|
{generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/dependency_cache.py
RENAMED
|
@@ -45,6 +45,16 @@ class DependencyCacheGetManyBackend(DependencyCacheBackend, Protocol):
|
|
|
45
45
|
...
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
class DependencyCacheSetManyBackend(DependencyCacheBackend, Protocol):
|
|
49
|
+
def set_many(
|
|
50
|
+
self,
|
|
51
|
+
data: Mapping[str, Any],
|
|
52
|
+
timeout: int | None = None,
|
|
53
|
+
) -> Any:
|
|
54
|
+
"""Store cached values for many keys."""
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
|
|
48
58
|
def make_dependency_cache_entry(
|
|
49
59
|
value: Any,
|
|
50
60
|
dependencies: Iterable[Dependency],
|
{generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/dependency_index.py
RENAMED
|
@@ -124,6 +124,14 @@ def _set_dependency_data_change_count(count: int) -> int:
|
|
|
124
124
|
return count
|
|
125
125
|
|
|
126
126
|
|
|
127
|
+
def _discard_active_context_dependency_cache_state() -> None:
|
|
128
|
+
from general_manager.cache.run_context import current_calculation_run_context
|
|
129
|
+
|
|
130
|
+
context = current_calculation_run_context()
|
|
131
|
+
if context is not None:
|
|
132
|
+
context.discard_dependency_cache_state()
|
|
133
|
+
|
|
134
|
+
|
|
127
135
|
def begin_dependency_data_change() -> int:
|
|
128
136
|
"""
|
|
129
137
|
Mark a data change as active and bump the dependency generation.
|
|
@@ -136,9 +144,17 @@ def begin_dependency_data_change() -> int:
|
|
|
136
144
|
generation = _set_dependency_generation(get_dependency_generation() + 1)
|
|
137
145
|
_set_dependency_data_change_count(_get_dependency_data_change_count() + 1)
|
|
138
146
|
cache.set(DATA_CHANGE_LOCK_KEY, "1", None)
|
|
139
|
-
return generation
|
|
140
147
|
finally:
|
|
141
148
|
release_lock()
|
|
149
|
+
try:
|
|
150
|
+
_discard_active_context_dependency_cache_state()
|
|
151
|
+
except Exception:
|
|
152
|
+
try:
|
|
153
|
+
end_dependency_data_change()
|
|
154
|
+
except Exception:
|
|
155
|
+
logger.exception("dependency data-change cleanup rollback failed")
|
|
156
|
+
raise
|
|
157
|
+
return generation
|
|
142
158
|
|
|
143
159
|
|
|
144
160
|
def end_dependency_data_change() -> None:
|
{generalmanager-0.48.0 → generalmanager-0.50.0}/src/general_manager/cache/dependency_publish.py
RENAMED
|
@@ -4,13 +4,15 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import time
|
|
6
6
|
import uuid
|
|
7
|
+
from collections import defaultdict
|
|
7
8
|
from dataclasses import dataclass
|
|
8
|
-
from typing import Any, Callable, Iterable
|
|
9
|
+
from typing import Any, Callable, Iterable, TypeGuard
|
|
9
10
|
|
|
10
11
|
from django.core.cache import cache as coordination_cache
|
|
11
12
|
|
|
12
13
|
from general_manager.cache.dependency_cache import (
|
|
13
14
|
DependencyCacheBackend,
|
|
15
|
+
DependencyCacheSetManyBackend,
|
|
14
16
|
make_dependency_cache_entry,
|
|
15
17
|
read_dependency_cache_hit,
|
|
16
18
|
)
|
|
@@ -39,6 +41,19 @@ class CacheComputeLease:
|
|
|
39
41
|
token: str
|
|
40
42
|
|
|
41
43
|
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class PendingDependencyCachePublication:
|
|
46
|
+
"""A dependency-cache miss waiting for guarded publication."""
|
|
47
|
+
|
|
48
|
+
cache_key: str
|
|
49
|
+
result: Any
|
|
50
|
+
dependencies: frozenset[Dependency]
|
|
51
|
+
cache_backend: DependencyCacheBackend
|
|
52
|
+
timeout: int | None
|
|
53
|
+
started_generation: int
|
|
54
|
+
lease: CacheComputeLease
|
|
55
|
+
|
|
56
|
+
|
|
42
57
|
RecordManyDependenciesFn = Callable[[Iterable[tuple[str, Iterable[Dependency]]]], None]
|
|
43
58
|
|
|
44
59
|
COMPUTE_LOCK_PREFIX = "dependency_cache_compute_lock"
|
|
@@ -111,6 +126,81 @@ def _ensure_publish_current(started_generation: int) -> None:
|
|
|
111
126
|
raise CachePublishAborted()
|
|
112
127
|
|
|
113
128
|
|
|
129
|
+
def _supports_set_many(
|
|
130
|
+
cache_backend: DependencyCacheBackend,
|
|
131
|
+
) -> TypeGuard[DependencyCacheSetManyBackend]:
|
|
132
|
+
return callable(getattr(cache_backend, "set_many", None))
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _set_dependency_cache_entries(
|
|
136
|
+
entries: Iterable[PendingDependencyCachePublication],
|
|
137
|
+
) -> None:
|
|
138
|
+
grouped: dict[
|
|
139
|
+
tuple[int, int | None],
|
|
140
|
+
list[PendingDependencyCachePublication],
|
|
141
|
+
] = defaultdict(list)
|
|
142
|
+
for entry in entries:
|
|
143
|
+
grouped[(id(entry.cache_backend), entry.timeout)].append(entry)
|
|
144
|
+
|
|
145
|
+
for group_entries in grouped.values():
|
|
146
|
+
cache_backend = group_entries[0].cache_backend
|
|
147
|
+
timeout = group_entries[0].timeout
|
|
148
|
+
payloads = {
|
|
149
|
+
entry.cache_key: make_dependency_cache_entry(
|
|
150
|
+
entry.result,
|
|
151
|
+
entry.dependencies,
|
|
152
|
+
)
|
|
153
|
+
for entry in group_entries
|
|
154
|
+
}
|
|
155
|
+
if _supports_set_many(cache_backend):
|
|
156
|
+
failed_keys = cache_backend.set_many(payloads, timeout) or ()
|
|
157
|
+
for key in failed_keys:
|
|
158
|
+
if key in payloads:
|
|
159
|
+
cache_backend.set(key, payloads[key], timeout)
|
|
160
|
+
continue
|
|
161
|
+
for key, payload in payloads.items():
|
|
162
|
+
cache_backend.set(key, payload, timeout)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def publish_dependency_cache_entries(
|
|
166
|
+
entries: Iterable[PendingDependencyCachePublication],
|
|
167
|
+
) -> None:
|
|
168
|
+
"""Publish dependency metadata and values for current entries in one batch."""
|
|
169
|
+
pending_entries = tuple(entries)
|
|
170
|
+
if not pending_entries:
|
|
171
|
+
return
|
|
172
|
+
|
|
173
|
+
acquire_lock_with_retry("publish_dependency_cache_entries")
|
|
174
|
+
try:
|
|
175
|
+
if is_dependency_data_change_active():
|
|
176
|
+
raise CachePublishAborted()
|
|
177
|
+
|
|
178
|
+
current_generation = get_dependency_generation()
|
|
179
|
+
publishable_entries = tuple(
|
|
180
|
+
entry
|
|
181
|
+
for entry in pending_entries
|
|
182
|
+
if entry.started_generation == current_generation
|
|
183
|
+
)
|
|
184
|
+
if not publishable_entries:
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
idx = get_full_index()
|
|
188
|
+
for entry in publishable_entries:
|
|
189
|
+
if entry.dependencies:
|
|
190
|
+
_record_dependencies_locked(
|
|
191
|
+
idx,
|
|
192
|
+
entry.cache_key,
|
|
193
|
+
entry.dependencies,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
_ensure_publish_current(current_generation)
|
|
197
|
+
set_full_index(idx)
|
|
198
|
+
_ensure_publish_current(current_generation)
|
|
199
|
+
_set_dependency_cache_entries(publishable_entries)
|
|
200
|
+
finally:
|
|
201
|
+
release_lock()
|
|
202
|
+
|
|
203
|
+
|
|
114
204
|
def publish_dependency_cache_entry(
|
|
115
205
|
*,
|
|
116
206
|
cache_key: str,
|
|
@@ -8,8 +8,13 @@ from contextvars import ContextVar, Token
|
|
|
8
8
|
from types import TracebackType
|
|
9
9
|
from typing import TYPE_CHECKING, Optional, TypeVar
|
|
10
10
|
|
|
11
|
+
from general_manager.logging import get_logger
|
|
12
|
+
|
|
11
13
|
if TYPE_CHECKING:
|
|
12
14
|
from general_manager.cache.dependency_cache import DependencyCacheHit
|
|
15
|
+
from general_manager.cache.dependency_publish import (
|
|
16
|
+
PendingDependencyCachePublication,
|
|
17
|
+
)
|
|
13
18
|
|
|
14
19
|
K = TypeVar("K", bound=Hashable)
|
|
15
20
|
T = TypeVar("T")
|
|
@@ -19,14 +24,27 @@ _active_context: ContextVar["CalculationRunContext | None"] = ContextVar(
|
|
|
19
24
|
default=None,
|
|
20
25
|
)
|
|
21
26
|
ORM_BUCKET_RESULT_PREFIX = "orm_bucket_result"
|
|
27
|
+
DEFAULT_DEPENDENCY_CACHE_PUBLISH_BATCH_SIZE = 1000
|
|
28
|
+
logger = get_logger("cache.run_context")
|
|
22
29
|
|
|
23
30
|
|
|
24
31
|
class CalculationRunContext:
|
|
25
32
|
"""Cache calculation work for one request, graph, bulk operation, or task."""
|
|
26
33
|
|
|
27
|
-
def __init__(
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
*,
|
|
37
|
+
dependency_cache_publish_batch_size: int = (
|
|
38
|
+
DEFAULT_DEPENDENCY_CACHE_PUBLISH_BATCH_SIZE
|
|
39
|
+
),
|
|
40
|
+
) -> None:
|
|
28
41
|
self._values: dict[Hashable, object] = {}
|
|
29
42
|
self._dependency_cache_hits: dict[str, DependencyCacheHit] = {}
|
|
43
|
+
self._dependency_cache_pending_publications: dict[
|
|
44
|
+
str,
|
|
45
|
+
PendingDependencyCachePublication,
|
|
46
|
+
] = {}
|
|
47
|
+
self._dependency_cache_publish_batch_size = dependency_cache_publish_batch_size
|
|
30
48
|
self._token: Token[CalculationRunContext | None] | None = None
|
|
31
49
|
|
|
32
50
|
def __enter__(self) -> "CalculationRunContext":
|
|
@@ -39,11 +57,18 @@ class CalculationRunContext:
|
|
|
39
57
|
exc_val: BaseException | None,
|
|
40
58
|
exc_tb: TracebackType | None,
|
|
41
59
|
) -> None:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
60
|
+
try:
|
|
61
|
+
if exc_type is None:
|
|
62
|
+
self.flush_dependency_cache_publications()
|
|
63
|
+
else:
|
|
64
|
+
self.discard_dependency_cache_publications()
|
|
65
|
+
finally:
|
|
66
|
+
if self._token is not None:
|
|
67
|
+
_active_context.reset(self._token)
|
|
68
|
+
self._token = None
|
|
69
|
+
self._values.clear()
|
|
70
|
+
self._dependency_cache_hits.clear()
|
|
71
|
+
self._dependency_cache_pending_publications.clear()
|
|
47
72
|
|
|
48
73
|
def get_or_set(self, key: Hashable, loader: Callable[[], T]) -> T:
|
|
49
74
|
"""Return a cached value for key, loading it once per active context."""
|
|
@@ -74,6 +99,73 @@ class CalculationRunContext:
|
|
|
74
99
|
"""Return a prefetched dependency-cache hit, or default when absent."""
|
|
75
100
|
return self._dependency_cache_hits.get(key, default)
|
|
76
101
|
|
|
102
|
+
def buffer_dependency_cache_publication(
|
|
103
|
+
self,
|
|
104
|
+
entry: PendingDependencyCachePublication,
|
|
105
|
+
) -> None:
|
|
106
|
+
"""Buffer a dependency-cache miss for guarded batch publication."""
|
|
107
|
+
from general_manager.cache.dependency_cache import DependencyCacheHit
|
|
108
|
+
from general_manager.cache.dependency_publish import release_compute_lease
|
|
109
|
+
|
|
110
|
+
previous_entry = self._dependency_cache_pending_publications.get(
|
|
111
|
+
entry.cache_key
|
|
112
|
+
)
|
|
113
|
+
if previous_entry is not None and previous_entry.lease != entry.lease:
|
|
114
|
+
release_compute_lease(previous_entry.lease)
|
|
115
|
+
self._dependency_cache_pending_publications[entry.cache_key] = entry
|
|
116
|
+
self._dependency_cache_hits[entry.cache_key] = DependencyCacheHit(
|
|
117
|
+
value=entry.result,
|
|
118
|
+
dependencies=entry.dependencies,
|
|
119
|
+
)
|
|
120
|
+
if (
|
|
121
|
+
len(self._dependency_cache_pending_publications)
|
|
122
|
+
>= self._dependency_cache_publish_batch_size
|
|
123
|
+
):
|
|
124
|
+
self.flush_dependency_cache_publications()
|
|
125
|
+
|
|
126
|
+
def flush_dependency_cache_publications(self) -> None:
|
|
127
|
+
"""Publish buffered dependency-cache misses and release their leases."""
|
|
128
|
+
entries = tuple(self._dependency_cache_pending_publications.values())
|
|
129
|
+
if not entries:
|
|
130
|
+
return
|
|
131
|
+
self._dependency_cache_pending_publications.clear()
|
|
132
|
+
|
|
133
|
+
from general_manager.cache.dependency_publish import (
|
|
134
|
+
CachePublishAborted,
|
|
135
|
+
publish_dependency_cache_entries,
|
|
136
|
+
release_compute_lease,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
try:
|
|
140
|
+
publish_dependency_cache_entries(entries)
|
|
141
|
+
except CachePublishAborted:
|
|
142
|
+
logger.debug(
|
|
143
|
+
"dependency cache batch publish aborted",
|
|
144
|
+
context={"entry_count": len(entries)},
|
|
145
|
+
)
|
|
146
|
+
finally:
|
|
147
|
+
for entry in entries:
|
|
148
|
+
release_compute_lease(entry.lease)
|
|
149
|
+
|
|
150
|
+
def discard_dependency_cache_publications(self) -> None:
|
|
151
|
+
"""Drop buffered dependency-cache misses and release their leases."""
|
|
152
|
+
entries = tuple(self._dependency_cache_pending_publications.values())
|
|
153
|
+
if not entries:
|
|
154
|
+
return
|
|
155
|
+
self._dependency_cache_pending_publications.clear()
|
|
156
|
+
|
|
157
|
+
from general_manager.cache.dependency_publish import release_compute_lease
|
|
158
|
+
|
|
159
|
+
for entry in entries:
|
|
160
|
+
release_compute_lease(entry.lease)
|
|
161
|
+
|
|
162
|
+
def discard_dependency_cache_state(self) -> None:
|
|
163
|
+
"""Drop dependency-cache hits and buffered publications for this run."""
|
|
164
|
+
try:
|
|
165
|
+
self.discard_dependency_cache_publications()
|
|
166
|
+
finally:
|
|
167
|
+
self._dependency_cache_hits.clear()
|
|
168
|
+
|
|
77
169
|
def discard_prefix(self, prefix: tuple[Hashable, ...]) -> None:
|
|
78
170
|
"""Discard tuple keys that start with the supplied prefix."""
|
|
79
171
|
for key in list(self._values):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{generalmanager-0.48.0 → generalmanager-0.50.0}/src/GeneralManager.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|