GeneralManager 0.52.1__tar.gz → 0.53.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.52.1/src/GeneralManager.egg-info → generalmanager-0.53.0}/PKG-INFO +1 -1
- {generalmanager-0.52.1 → generalmanager-0.53.0}/pyproject.toml +1 -1
- {generalmanager-0.52.1 → generalmanager-0.53.0/src/GeneralManager.egg-info}/PKG-INFO +1 -1
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/bucket/database_bucket.py +117 -14
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/cache/dependency_shards.py +199 -50
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/cache/run_context.py +10 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/orm_interface.py +29 -1
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/manager/general_manager.py +51 -1
- {generalmanager-0.52.1 → generalmanager-0.53.0}/LICENSE +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/README.md +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/setup.cfg +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/GeneralManager.egg-info/SOURCES.txt +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/GeneralManager.egg-info/requires.txt +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/GeneralManager.egg-info/top_level.txt +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/_types/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/_types/api.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/_types/bucket.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/_types/cache.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/_types/factory.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/_types/general_manager.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/_types/interface.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/_types/manager.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/_types/measurement.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/_types/permission.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/_types/rule.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/_types/search.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/_types/utils.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/api/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/api/graphql.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/api/graphql_errors.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/api/graphql_mutations.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/api/graphql_prefetch.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/api/graphql_resolvers.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/api/graphql_search.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/api/graphql_subscriptions.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/api/graphql_view.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/api/mutation.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/api/property.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/api/registry.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/api/remote_api.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/api/remote_invalidation.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/api/remote_invalidation_client.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/apps.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/bootstrap.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/bucket/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/bucket/base_bucket.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/bucket/calculation_bucket.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/bucket/group_bucket.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/bucket/indexing.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/bucket/request_bucket.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/cache/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/cache/cache_decorator.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/cache/cache_tracker.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/cache/dependency_cache.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/cache/dependency_index.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/cache/dependency_matching.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/cache/dependency_publish.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/cache/model_dependency_collector.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/cache/signals.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/conf.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/factory/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/factory/auto_factory.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/factory/factories.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/factory/factory_methods.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/base_interface.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/bundles/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/bundles/calculation.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/bundles/database.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/bundles/remote_manager.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/bundles/request.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/base.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/builtin.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/configuration.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/core/observability.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/core/utils.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/exceptions.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/factory.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/orm/history.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/orm/mutations.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/orm/support.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/registry.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/infrastructure/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/interfaces/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/interfaces/calculation.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/interfaces/database.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/interfaces/existing_model.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/interfaces/read_only.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/interfaces/request.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/manifests/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/manifests/capability_builder.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/manifests/capability_models.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/requests.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/utils/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/utils/errors.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/interface/utils/models.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/logging.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/management/commands/search_index.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/management/commands/shell.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/manager/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/manager/group_manager.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/manager/input.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/manager/meta.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/measurement/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/measurement/measurement.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/measurement/measurement_field.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/metrics/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/metrics/graphql.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/migrations/0001_initial.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/migrations/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/models.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/permission/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/permission/audit.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/permission/base_permission.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/permission/graphql_capabilities.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/permission/manager_based_permission.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/permission/mutation_permission.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/permission/permission_checks.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/permission/permission_data_manager.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/permission/utils.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/public_api_registry.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/py.typed +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/rule/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/rule/handler.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/rule/rule.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/search/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/search/async_tasks.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/search/backend.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/search/backend_registry.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/search/backends/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/search/backends/dev.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/search/backends/meilisearch.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/search/backends/opensearch.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/search/backends/typesense.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/search/config.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/search/indexer.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/search/registry.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/search/utils.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/seeding/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/seeding/manager_landscape.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/utils/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/utils/args_to_kwargs.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/utils/filter_parser.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/utils/format_string.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/utils/json_encoder.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/utils/make_cache_key.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/utils/none_to_zero.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/utils/path_mapping.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/utils/public_api.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/utils/testing.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/utils/type_checks.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/workflow/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/workflow/actions.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/workflow/backend_registry.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/workflow/backends/__init__.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/workflow/backends/celery.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/workflow/backends/local.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/workflow/backends/n8n.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/workflow/config.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/workflow/engine.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/workflow/event_registry.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/workflow/events.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/workflow/models.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/workflow/signal_bridge.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/workflow/tasks.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/workflow/telemetry.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/tests/test_settings.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.0}/tests/test_urls.py +0 -0
- {generalmanager-0.52.1 → generalmanager-0.53.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.53.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.53.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.53.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
|
{generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/bucket/database_bucket.py
RENAMED
|
@@ -257,11 +257,44 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
|
257
257
|
),
|
|
258
258
|
)
|
|
259
259
|
|
|
260
|
-
def
|
|
260
|
+
def _build_manager_from_primary_key(self, pk: Any) -> GeneralManagerType:
|
|
261
261
|
if self._search_date is None:
|
|
262
262
|
return self._manager_class(pk)
|
|
263
263
|
return self._manager_class(pk, search_date=self._search_date)
|
|
264
264
|
|
|
265
|
+
def _can_trust_orm_instance(self, instance: models.Model) -> bool:
|
|
266
|
+
interface_model = getattr(self._manager_class.Interface, "_model", None)
|
|
267
|
+
if isinstance(interface_model, type) and issubclass(
|
|
268
|
+
interface_model, models.Model
|
|
269
|
+
):
|
|
270
|
+
if not isinstance(instance, interface_model):
|
|
271
|
+
return False
|
|
272
|
+
get_deferred_fields = getattr(instance, "get_deferred_fields", None)
|
|
273
|
+
if callable(get_deferred_fields) and get_deferred_fields():
|
|
274
|
+
return False
|
|
275
|
+
if self._search_date is not None and not (
|
|
276
|
+
hasattr(instance, "_history") or hasattr(instance, "history_date")
|
|
277
|
+
):
|
|
278
|
+
return False
|
|
279
|
+
return True
|
|
280
|
+
|
|
281
|
+
def _build_manager_from_instance(
|
|
282
|
+
self,
|
|
283
|
+
instance: models.Model,
|
|
284
|
+
) -> GeneralManagerType:
|
|
285
|
+
interface_hydrate = getattr(
|
|
286
|
+
self._manager_class.Interface, "_from_trusted_orm_instance", None
|
|
287
|
+
)
|
|
288
|
+
manager_hydrate = self._manager_class._from_trusted_orm_instance
|
|
289
|
+
if callable(interface_hydrate) and self._can_trust_orm_instance(instance):
|
|
290
|
+
return manager_hydrate(instance, search_date=self._search_date)
|
|
291
|
+
return self._build_manager_from_primary_key(instance.pk)
|
|
292
|
+
|
|
293
|
+
def _build_manager(self, value: Any) -> GeneralManagerType:
|
|
294
|
+
if isinstance(value, models.Model):
|
|
295
|
+
return self._build_manager_from_instance(value)
|
|
296
|
+
return self._build_manager_from_primary_key(value)
|
|
297
|
+
|
|
265
298
|
@staticmethod
|
|
266
299
|
def _copy_filter_definitions(
|
|
267
300
|
definitions: dict[str, Any] | None,
|
|
@@ -314,6 +347,11 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
|
314
347
|
return None
|
|
315
348
|
if query.distinct:
|
|
316
349
|
return None
|
|
350
|
+
if getattr(self._data, "_prefetch_related_lookups", ()):
|
|
351
|
+
return None
|
|
352
|
+
deferred_loading = getattr(query, "deferred_loading", None)
|
|
353
|
+
if isinstance(deferred_loading, tuple) and deferred_loading[0]:
|
|
354
|
+
return None
|
|
317
355
|
try:
|
|
318
356
|
sql, params = self._data.query.sql_with_params()
|
|
319
357
|
except (EmptyResultSet, FieldError, TypeError, ValueError):
|
|
@@ -370,6 +408,28 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
|
370
408
|
context.set_orm_bucket_result(signature, primary_keys)
|
|
371
409
|
return primary_keys
|
|
372
410
|
|
|
411
|
+
def _get_run_scoped_rows(self) -> tuple[models.Model, ...] | None:
|
|
412
|
+
"""
|
|
413
|
+
Load or reuse a bounded model-row snapshot for trusted hydration.
|
|
414
|
+
"""
|
|
415
|
+
context = current_calculation_run_context()
|
|
416
|
+
if context is None:
|
|
417
|
+
return None
|
|
418
|
+
signature = self._query_signature()
|
|
419
|
+
if signature is None:
|
|
420
|
+
return None
|
|
421
|
+
cached = context.get_orm_bucket_rows(signature)
|
|
422
|
+
if cached is not None:
|
|
423
|
+
return cached # type: ignore[return-value]
|
|
424
|
+
|
|
425
|
+
rows = tuple(self._data[: MAX_RUN_SCOPED_BUCKET_RESULT_ROWS + 1])
|
|
426
|
+
if len(rows) > MAX_RUN_SCOPED_BUCKET_RESULT_ROWS:
|
|
427
|
+
return None
|
|
428
|
+
primary_keys = tuple(row.pk for row in rows)
|
|
429
|
+
context.set_orm_bucket_rows(signature, rows)
|
|
430
|
+
context.set_orm_bucket_result(signature, primary_keys)
|
|
431
|
+
return rows
|
|
432
|
+
|
|
373
433
|
def _peek_run_scoped_primary_keys(self) -> tuple[Any, ...] | None:
|
|
374
434
|
"""
|
|
375
435
|
Return an already cached primary-key snapshot without evaluating the ORM.
|
|
@@ -393,6 +453,19 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
|
393
453
|
return None
|
|
394
454
|
return cached # type: ignore[return-value]
|
|
395
455
|
|
|
456
|
+
def _peek_run_scoped_rows(self) -> tuple[models.Model, ...] | None:
|
|
457
|
+
"""Return cached model rows without evaluating the ORM."""
|
|
458
|
+
context = current_calculation_run_context()
|
|
459
|
+
if context is None:
|
|
460
|
+
return None
|
|
461
|
+
signature = self._query_signature()
|
|
462
|
+
if signature is None:
|
|
463
|
+
return None
|
|
464
|
+
cached = context.get_orm_bucket_rows(signature)
|
|
465
|
+
if cached is None:
|
|
466
|
+
return None
|
|
467
|
+
return cached # type: ignore[return-value]
|
|
468
|
+
|
|
396
469
|
@staticmethod
|
|
397
470
|
def _snapshot_get_primary_key(kwargs: dict[str, Any]) -> Any | None:
|
|
398
471
|
"""
|
|
@@ -458,13 +531,18 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
|
458
531
|
GeneralManagerType: Manager instance for each primary key in the queryset.
|
|
459
532
|
"""
|
|
460
533
|
self._track_effective_dependencies()
|
|
461
|
-
|
|
534
|
+
rows = self._get_run_scoped_rows()
|
|
535
|
+
if rows is not None:
|
|
536
|
+
for row in rows:
|
|
537
|
+
yield self._build_manager_from_instance(row)
|
|
538
|
+
return
|
|
539
|
+
primary_keys = self._peek_run_scoped_primary_keys()
|
|
462
540
|
if primary_keys is not None:
|
|
463
541
|
for primary_key in primary_keys:
|
|
464
|
-
yield self.
|
|
542
|
+
yield self._build_manager_from_primary_key(primary_key)
|
|
465
543
|
return
|
|
466
544
|
for item in self._data:
|
|
467
|
-
yield self.
|
|
545
|
+
yield self._build_manager_from_instance(item)
|
|
468
546
|
|
|
469
547
|
def __or__(
|
|
470
548
|
self,
|
|
@@ -585,7 +663,7 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
|
585
663
|
"""
|
|
586
664
|
ids: list[int] = []
|
|
587
665
|
for obj in query_set:
|
|
588
|
-
inst = self.
|
|
666
|
+
inst = self._build_manager_from_instance(obj)
|
|
589
667
|
keep = True
|
|
590
668
|
for k, val, root in python_filters:
|
|
591
669
|
lookup = k.split("__", 1)[1] if "__" in k else ""
|
|
@@ -704,15 +782,20 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
|
704
782
|
GeneralManagerType | None: First manager instance if available.
|
|
705
783
|
"""
|
|
706
784
|
self._track_effective_dependencies()
|
|
785
|
+
rows = self._peek_run_scoped_rows()
|
|
786
|
+
if rows is not None:
|
|
787
|
+
if not rows:
|
|
788
|
+
return None
|
|
789
|
+
return self._build_manager_from_instance(rows[0])
|
|
707
790
|
primary_keys = self._peek_run_scoped_primary_keys()
|
|
708
791
|
if primary_keys is not None:
|
|
709
792
|
if not primary_keys:
|
|
710
793
|
return None
|
|
711
|
-
return self.
|
|
794
|
+
return self._build_manager_from_primary_key(primary_keys[0])
|
|
712
795
|
first_element = self._data.first()
|
|
713
796
|
if first_element is None:
|
|
714
797
|
return None
|
|
715
|
-
return self.
|
|
798
|
+
return self._build_manager_from_instance(first_element)
|
|
716
799
|
|
|
717
800
|
def last(self) -> GeneralManagerType | None:
|
|
718
801
|
"""
|
|
@@ -722,15 +805,20 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
|
722
805
|
GeneralManagerType | None: Last manager instance if available.
|
|
723
806
|
"""
|
|
724
807
|
self._track_effective_dependencies()
|
|
808
|
+
rows = self._peek_run_scoped_rows()
|
|
809
|
+
if rows is not None:
|
|
810
|
+
if not rows:
|
|
811
|
+
return None
|
|
812
|
+
return self._build_manager_from_instance(rows[-1])
|
|
725
813
|
primary_keys = self._peek_run_scoped_primary_keys()
|
|
726
814
|
if primary_keys is not None:
|
|
727
815
|
if not primary_keys:
|
|
728
816
|
return None
|
|
729
|
-
return self.
|
|
817
|
+
return self._build_manager_from_primary_key(primary_keys[-1])
|
|
730
818
|
first_element = self._data.last()
|
|
731
819
|
if first_element is None:
|
|
732
820
|
return None
|
|
733
|
-
return self.
|
|
821
|
+
return self._build_manager_from_instance(first_element)
|
|
734
822
|
|
|
735
823
|
def count(self) -> int:
|
|
736
824
|
"""
|
|
@@ -778,18 +866,28 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
|
778
866
|
models.MultipleObjectsReturned: Propagated when multiple rows satisfy the lookup.
|
|
779
867
|
"""
|
|
780
868
|
self._track_effective_dependencies()
|
|
869
|
+
rows = self._peek_run_scoped_rows()
|
|
870
|
+
if rows is not None:
|
|
871
|
+
requested_primary_key = self._snapshot_get_primary_key(kwargs)
|
|
872
|
+
if requested_primary_key is not None:
|
|
873
|
+
matches = [row for row in rows if row.pk == requested_primary_key]
|
|
874
|
+
if len(matches) == 1:
|
|
875
|
+
return self._build_manager_from_instance(matches[0])
|
|
876
|
+
if len(matches) > 1:
|
|
877
|
+
raise self._data.model.MultipleObjectsReturned
|
|
878
|
+
raise self._data.model.DoesNotExist
|
|
781
879
|
primary_keys = self._peek_run_scoped_primary_keys()
|
|
782
880
|
if primary_keys is not None:
|
|
783
881
|
requested_primary_key = self._snapshot_get_primary_key(kwargs)
|
|
784
882
|
if requested_primary_key is not None:
|
|
785
883
|
match_count = primary_keys.count(requested_primary_key)
|
|
786
884
|
if match_count == 1:
|
|
787
|
-
return self.
|
|
885
|
+
return self._build_manager_from_primary_key(requested_primary_key)
|
|
788
886
|
if match_count > 1:
|
|
789
887
|
raise self._data.model.MultipleObjectsReturned
|
|
790
888
|
raise self._data.model.DoesNotExist
|
|
791
889
|
element = self._data.get(**kwargs)
|
|
792
|
-
return self.
|
|
890
|
+
return self._build_manager_from_instance(element)
|
|
793
891
|
|
|
794
892
|
def __getitem__(
|
|
795
893
|
self, item: int | slice
|
|
@@ -815,12 +913,17 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
|
815
913
|
run_scoped_cacheable=self._run_scoped_cacheable,
|
|
816
914
|
)
|
|
817
915
|
self._track_effective_dependencies()
|
|
916
|
+
rows = self._peek_run_scoped_rows()
|
|
917
|
+
if rows is not None:
|
|
918
|
+
if item < 0:
|
|
919
|
+
raise ValueError
|
|
920
|
+
return self._build_manager_from_instance(rows[item])
|
|
818
921
|
primary_keys = self._peek_run_scoped_primary_keys()
|
|
819
922
|
if primary_keys is not None:
|
|
820
923
|
if item < 0:
|
|
821
924
|
raise ValueError
|
|
822
|
-
return self.
|
|
823
|
-
return self.
|
|
925
|
+
return self._build_manager_from_primary_key(primary_keys[item])
|
|
926
|
+
return self._build_manager_from_instance(self._data[item])
|
|
824
927
|
|
|
825
928
|
def __len__(self) -> int:
|
|
826
929
|
"""
|
|
@@ -926,7 +1029,7 @@ class DatabaseBucket(Bucket[GeneralManagerType]):
|
|
|
926
1029
|
objs = list(qs)
|
|
927
1030
|
|
|
928
1031
|
def key_func(obj: models.Model) -> tuple[object, ...]:
|
|
929
|
-
inst = self.
|
|
1032
|
+
inst = self._build_manager_from_instance(obj)
|
|
930
1033
|
values = []
|
|
931
1034
|
for k in key:
|
|
932
1035
|
if k in properties:
|
{generalmanager-0.52.1 → generalmanager-0.53.0}/src/general_manager/cache/dependency_shards.py
RENAMED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import hashlib
|
|
6
|
+
from collections import defaultdict
|
|
6
7
|
from dataclasses import dataclass, field
|
|
7
|
-
from typing import Any, Iterable, Literal
|
|
8
|
+
from typing import Any, Iterable, Literal, Mapping
|
|
8
9
|
|
|
9
10
|
from django.core.cache import cache
|
|
10
11
|
|
|
@@ -37,6 +38,14 @@ class ReverseDependencyMembership:
|
|
|
37
38
|
simple_dependencies: frozenset[SimpleDependency] = field(default_factory=frozenset)
|
|
38
39
|
|
|
39
40
|
|
|
41
|
+
@dataclass(frozen=True, slots=True)
|
|
42
|
+
class _DependencyShardPlan:
|
|
43
|
+
shard_keys: frozenset[str]
|
|
44
|
+
composite_dependencies: frozenset[CompositeDependency]
|
|
45
|
+
simple_dependencies: frozenset[SimpleDependency]
|
|
46
|
+
lookup_registrations: frozenset[tuple[str, str, str]]
|
|
47
|
+
|
|
48
|
+
|
|
40
49
|
def _hash_text(value: str) -> str:
|
|
41
50
|
return hashlib.sha256(value.encode("utf-8")).hexdigest()
|
|
42
51
|
|
|
@@ -104,19 +113,78 @@ def cache_set_members(key: str) -> set[str]:
|
|
|
104
113
|
return set(members)
|
|
105
114
|
|
|
106
115
|
|
|
116
|
+
def _cache_get_many(keys: Iterable[str]) -> dict[str, Any]:
|
|
117
|
+
key_tuple = tuple(dict.fromkeys(keys))
|
|
118
|
+
if not key_tuple:
|
|
119
|
+
return {}
|
|
120
|
+
return dict(cache.get_many(key_tuple))
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _cache_set_many(payloads: Mapping[str, Any]) -> None:
|
|
124
|
+
if payloads:
|
|
125
|
+
cache.set_many(dict(payloads), None)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _cache_delete_many(keys: Iterable[str]) -> None:
|
|
129
|
+
key_tuple = tuple(dict.fromkeys(keys))
|
|
130
|
+
if not key_tuple:
|
|
131
|
+
return
|
|
132
|
+
delete_many = getattr(cache, "delete_many", None)
|
|
133
|
+
if callable(delete_many):
|
|
134
|
+
delete_many(key_tuple)
|
|
135
|
+
return
|
|
136
|
+
for key in key_tuple:
|
|
137
|
+
cache.delete(key)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _cache_member_set(value: Any) -> set[str]:
|
|
141
|
+
if value is None:
|
|
142
|
+
return set()
|
|
143
|
+
if isinstance(value, (set, frozenset, list, tuple)):
|
|
144
|
+
return {member for member in value if isinstance(member, str)}
|
|
145
|
+
return set()
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _cache_set_add_many(additions: Mapping[str, set[str]]) -> None:
|
|
149
|
+
pending = {key: set(members) for key, members in additions.items() if members}
|
|
150
|
+
if not pending:
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
existing_sets = _cache_get_many(pending)
|
|
154
|
+
payloads: dict[str, set[str]] = {}
|
|
155
|
+
for key, members in pending.items():
|
|
156
|
+
current = _cache_member_set(existing_sets.get(key, set()))
|
|
157
|
+
current.update(members)
|
|
158
|
+
payloads[key] = current
|
|
159
|
+
_cache_set_many(payloads)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _cache_set_discard_many(removals: Mapping[str, set[str]]) -> None:
|
|
163
|
+
pending = {key: set(members) for key, members in removals.items() if members}
|
|
164
|
+
if not pending:
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
existing_sets = _cache_get_many(pending)
|
|
168
|
+
payloads: dict[str, set[str]] = {}
|
|
169
|
+
delete_keys: list[str] = []
|
|
170
|
+
for key, members in pending.items():
|
|
171
|
+
current = _cache_member_set(existing_sets.get(key, set()))
|
|
172
|
+
current.difference_update(members)
|
|
173
|
+
if current:
|
|
174
|
+
payloads[key] = current
|
|
175
|
+
else:
|
|
176
|
+
delete_keys.append(key)
|
|
177
|
+
|
|
178
|
+
_cache_set_many(payloads)
|
|
179
|
+
_cache_delete_many(delete_keys)
|
|
180
|
+
|
|
181
|
+
|
|
107
182
|
def _cache_set_add(key: str, member: str) -> None:
|
|
108
|
-
|
|
109
|
-
members.add(member)
|
|
110
|
-
cache.set(key, members, None)
|
|
183
|
+
_cache_set_add_many({key: {member}})
|
|
111
184
|
|
|
112
185
|
|
|
113
186
|
def _cache_set_discard(key: str, member: str) -> None:
|
|
114
|
-
|
|
115
|
-
members.discard(member)
|
|
116
|
-
if members:
|
|
117
|
-
cache.set(key, members, None)
|
|
118
|
-
else:
|
|
119
|
-
cache.delete(key)
|
|
187
|
+
_cache_set_discard_many({key: {member}})
|
|
120
188
|
|
|
121
189
|
|
|
122
190
|
def _lookup_name_for_candidate(lookup: str) -> str:
|
|
@@ -190,20 +258,31 @@ def _shard_keys_for_dependency(
|
|
|
190
258
|
manager_name: str,
|
|
191
259
|
action: DependencyAction,
|
|
192
260
|
identifier: str,
|
|
193
|
-
) ->
|
|
261
|
+
) -> _DependencyShardPlan:
|
|
194
262
|
shard_keys: set[str] = set()
|
|
195
263
|
composites: set[CompositeDependency] = set()
|
|
196
264
|
simple_dependencies: set[SimpleDependency] = set()
|
|
265
|
+
lookup_registrations: set[tuple[str, str, str]] = set()
|
|
197
266
|
|
|
198
267
|
if action == "all":
|
|
199
268
|
shard_keys.add(all_records_shard_key(manager_name))
|
|
200
269
|
simple_dependencies.add((manager_name, action, identifier))
|
|
201
|
-
return
|
|
270
|
+
return _DependencyShardPlan(
|
|
271
|
+
frozenset(shard_keys),
|
|
272
|
+
frozenset(composites),
|
|
273
|
+
frozenset(simple_dependencies),
|
|
274
|
+
frozenset(lookup_registrations),
|
|
275
|
+
)
|
|
202
276
|
|
|
203
277
|
if action == "request_query":
|
|
204
278
|
shard_keys.add(request_query_shard_key(manager_name))
|
|
205
279
|
simple_dependencies.add((manager_name, action, identifier))
|
|
206
|
-
return
|
|
280
|
+
return _DependencyShardPlan(
|
|
281
|
+
frozenset(shard_keys),
|
|
282
|
+
frozenset(composites),
|
|
283
|
+
frozenset(simple_dependencies),
|
|
284
|
+
frozenset(lookup_registrations),
|
|
285
|
+
)
|
|
207
286
|
|
|
208
287
|
if action == "identification":
|
|
209
288
|
shard_keys.add(
|
|
@@ -216,19 +295,39 @@ def _shard_keys_for_dependency(
|
|
|
216
295
|
)
|
|
217
296
|
)
|
|
218
297
|
simple_dependencies.add((manager_name, action, identifier))
|
|
219
|
-
return
|
|
298
|
+
return _DependencyShardPlan(
|
|
299
|
+
frozenset(shard_keys),
|
|
300
|
+
frozenset(composites),
|
|
301
|
+
frozenset(simple_dependencies),
|
|
302
|
+
frozenset(lookup_registrations),
|
|
303
|
+
)
|
|
220
304
|
|
|
221
305
|
if action not in {"filter", "exclude"}:
|
|
222
|
-
return
|
|
306
|
+
return _DependencyShardPlan(
|
|
307
|
+
frozenset(),
|
|
308
|
+
frozenset(),
|
|
309
|
+
frozenset(),
|
|
310
|
+
frozenset(),
|
|
311
|
+
)
|
|
223
312
|
|
|
224
313
|
params = parse_dependency_identifier(identifier)
|
|
225
314
|
if not isinstance(params, dict):
|
|
226
|
-
return
|
|
315
|
+
return _DependencyShardPlan(
|
|
316
|
+
frozenset(),
|
|
317
|
+
frozenset(),
|
|
318
|
+
frozenset(),
|
|
319
|
+
frozenset(),
|
|
320
|
+
)
|
|
227
321
|
|
|
228
322
|
if not params:
|
|
229
323
|
shard_keys.add(all_records_shard_key(manager_name))
|
|
230
324
|
simple_dependencies.add((manager_name, action, identifier))
|
|
231
|
-
return
|
|
325
|
+
return _DependencyShardPlan(
|
|
326
|
+
frozenset(shard_keys),
|
|
327
|
+
frozenset(composites),
|
|
328
|
+
frozenset(simple_dependencies),
|
|
329
|
+
frozenset(lookup_registrations),
|
|
330
|
+
)
|
|
232
331
|
|
|
233
332
|
sort_lookups = [
|
|
234
333
|
str(lookup).removeprefix("__sort__")
|
|
@@ -239,18 +338,23 @@ def _shard_keys_for_dependency(
|
|
|
239
338
|
composite = (manager_name, action, identifier)
|
|
240
339
|
composites.add(composite)
|
|
241
340
|
for sort_lookup in sort_lookups:
|
|
242
|
-
|
|
341
|
+
lookup_registrations.add((manager_name, action, sort_lookup))
|
|
243
342
|
shard_keys.add(
|
|
244
343
|
composite_lookup_shard_key(manager_name, action, sort_lookup)
|
|
245
344
|
)
|
|
246
|
-
return
|
|
345
|
+
return _DependencyShardPlan(
|
|
346
|
+
frozenset(shard_keys),
|
|
347
|
+
frozenset(composites),
|
|
348
|
+
frozenset(simple_dependencies),
|
|
349
|
+
frozenset(lookup_registrations),
|
|
350
|
+
)
|
|
247
351
|
|
|
248
352
|
if len(params) > 1:
|
|
249
353
|
composite = (manager_name, action, identifier)
|
|
250
354
|
composites.add(composite)
|
|
251
355
|
for lookup in params:
|
|
252
356
|
candidate_lookup = _lookup_name_for_candidate(str(lookup))
|
|
253
|
-
|
|
357
|
+
lookup_registrations.add((manager_name, action, candidate_lookup))
|
|
254
358
|
shard_keys.add(
|
|
255
359
|
composite_lookup_shard_key(
|
|
256
360
|
manager_name,
|
|
@@ -258,12 +362,17 @@ def _shard_keys_for_dependency(
|
|
|
258
362
|
candidate_lookup,
|
|
259
363
|
)
|
|
260
364
|
)
|
|
261
|
-
return
|
|
365
|
+
return _DependencyShardPlan(
|
|
366
|
+
frozenset(shard_keys),
|
|
367
|
+
frozenset(composites),
|
|
368
|
+
frozenset(simple_dependencies),
|
|
369
|
+
frozenset(lookup_registrations),
|
|
370
|
+
)
|
|
262
371
|
|
|
263
372
|
lookup, value = next(iter(params.items()))
|
|
264
373
|
spec = lookup_spec_from_key(str(lookup))
|
|
265
374
|
candidate_lookup = "__".join(spec.attr_path)
|
|
266
|
-
|
|
375
|
+
lookup_registrations.add((manager_name, action, candidate_lookup))
|
|
267
376
|
if spec.operator == "eq":
|
|
268
377
|
shard_keys.add(
|
|
269
378
|
exact_lookup_shard_key(manager_name, action, spec.lookup, "eq", value)
|
|
@@ -273,56 +382,96 @@ def _shard_keys_for_dependency(
|
|
|
273
382
|
scan_lookup_shard_key(manager_name, action, spec.lookup, spec.operator)
|
|
274
383
|
)
|
|
275
384
|
simple_dependencies.add((manager_name, action, identifier))
|
|
276
|
-
return
|
|
385
|
+
return _DependencyShardPlan(
|
|
386
|
+
frozenset(shard_keys),
|
|
387
|
+
frozenset(composites),
|
|
388
|
+
frozenset(simple_dependencies),
|
|
389
|
+
frozenset(lookup_registrations),
|
|
390
|
+
)
|
|
277
391
|
|
|
278
392
|
|
|
279
|
-
def
|
|
393
|
+
def _reverse_membership_for_dependencies(
|
|
280
394
|
cache_key: str,
|
|
281
|
-
|
|
282
|
-
) ->
|
|
283
|
-
"""Record dependency metadata for one cache key in deterministic shards."""
|
|
284
|
-
dependency_set = set(dependencies)
|
|
285
|
-
if not dependency_set:
|
|
286
|
-
return
|
|
287
|
-
|
|
288
|
-
clear_legacy_dependency_index()
|
|
289
|
-
remove_cache_key_from_shards(cache_key)
|
|
290
|
-
|
|
395
|
+
dependency_set: set[Dependency],
|
|
396
|
+
) -> tuple[ReverseDependencyMembership, set[tuple[str, str, str]]]:
|
|
291
397
|
shard_keys: set[str] = set()
|
|
292
398
|
composites: set[CompositeDependency] = set()
|
|
293
399
|
simple_dependencies: set[SimpleDependency] = set()
|
|
294
|
-
|
|
295
|
-
new_shards, new_composites, new_simple = _shard_keys_for_dependency(
|
|
296
|
-
manager_name,
|
|
297
|
-
action,
|
|
298
|
-
identifier,
|
|
299
|
-
)
|
|
300
|
-
shard_keys.update(new_shards)
|
|
301
|
-
composites.update(new_composites)
|
|
302
|
-
simple_dependencies.update(new_simple)
|
|
400
|
+
lookup_registrations: set[tuple[str, str, str]] = set()
|
|
303
401
|
|
|
304
|
-
for
|
|
305
|
-
|
|
402
|
+
for manager_name, action, identifier in dependency_set:
|
|
403
|
+
plan = _shard_keys_for_dependency(manager_name, action, identifier)
|
|
404
|
+
shard_keys.update(plan.shard_keys)
|
|
405
|
+
composites.update(plan.composite_dependencies)
|
|
406
|
+
simple_dependencies.update(plan.simple_dependencies)
|
|
407
|
+
lookup_registrations.update(plan.lookup_registrations)
|
|
306
408
|
|
|
307
|
-
|
|
308
|
-
reverse_membership_key(cache_key),
|
|
409
|
+
return (
|
|
309
410
|
ReverseDependencyMembership(
|
|
310
411
|
cache_key=cache_key,
|
|
311
412
|
shard_keys=frozenset(shard_keys),
|
|
312
413
|
composite_dependencies=frozenset(composites),
|
|
313
414
|
simple_dependencies=frozenset(simple_dependencies),
|
|
314
415
|
),
|
|
315
|
-
|
|
416
|
+
lookup_registrations,
|
|
316
417
|
)
|
|
317
|
-
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def record_cache_dependencies(
|
|
421
|
+
cache_key: str,
|
|
422
|
+
dependencies: Iterable[Dependency],
|
|
423
|
+
) -> None:
|
|
424
|
+
"""Record dependency metadata for one cache key in deterministic shards."""
|
|
425
|
+
record_many_cache_dependencies(((cache_key, dependencies),))
|
|
318
426
|
|
|
319
427
|
|
|
320
428
|
def record_many_cache_dependencies(
|
|
321
429
|
entries: Iterable[tuple[str, Iterable[Dependency]]],
|
|
322
430
|
) -> None:
|
|
323
431
|
"""Record dependency metadata for many cache keys."""
|
|
432
|
+
normalized: dict[str, set[Dependency]] = {}
|
|
324
433
|
for cache_key, dependencies in entries:
|
|
325
|
-
|
|
434
|
+
dependency_set = set(dependencies)
|
|
435
|
+
if dependency_set:
|
|
436
|
+
normalized.setdefault(cache_key, set()).update(dependency_set)
|
|
437
|
+
if not normalized:
|
|
438
|
+
return
|
|
439
|
+
|
|
440
|
+
clear_legacy_dependency_index()
|
|
441
|
+
|
|
442
|
+
reverse_keys = {
|
|
443
|
+
cache_key: reverse_membership_key(cache_key) for cache_key in normalized
|
|
444
|
+
}
|
|
445
|
+
existing_reverses = _cache_get_many(reverse_keys.values())
|
|
446
|
+
|
|
447
|
+
shard_removals: dict[str, set[str]] = defaultdict(set)
|
|
448
|
+
for cache_key, reverse_key in reverse_keys.items():
|
|
449
|
+
reverse = existing_reverses.get(reverse_key)
|
|
450
|
+
if isinstance(reverse, ReverseDependencyMembership):
|
|
451
|
+
for shard_key in reverse.shard_keys:
|
|
452
|
+
shard_removals[shard_key].add(cache_key)
|
|
453
|
+
|
|
454
|
+
_cache_set_discard_many(shard_removals)
|
|
455
|
+
|
|
456
|
+
set_additions: dict[str, set[str]] = defaultdict(set)
|
|
457
|
+
reverse_payloads: dict[str, ReverseDependencyMembership] = {}
|
|
458
|
+
|
|
459
|
+
for cache_key, dependency_set in normalized.items():
|
|
460
|
+
reverse, lookup_registrations = _reverse_membership_for_dependencies(
|
|
461
|
+
cache_key,
|
|
462
|
+
dependency_set,
|
|
463
|
+
)
|
|
464
|
+
for shard_key in reverse.shard_keys:
|
|
465
|
+
set_additions[shard_key].add(cache_key)
|
|
466
|
+
for manager_name, action, lookup in lookup_registrations:
|
|
467
|
+
set_additions[lookup_registry_key(manager_name, action)].add(lookup)
|
|
468
|
+
|
|
469
|
+
reverse_key = reverse_keys[cache_key]
|
|
470
|
+
reverse_payloads[reverse_key] = reverse
|
|
471
|
+
set_additions[REVERSE_MEMBERSHIP_REGISTRY_KEY].add(reverse_key)
|
|
472
|
+
|
|
473
|
+
_cache_set_add_many(set_additions)
|
|
474
|
+
_cache_set_many(reverse_payloads)
|
|
326
475
|
|
|
327
476
|
|
|
328
477
|
def remove_cache_key_from_shards(cache_key: str) -> None:
|
|
@@ -26,6 +26,7 @@ _active_context: ContextVar["CalculationRunContext | None"] = ContextVar(
|
|
|
26
26
|
default=None,
|
|
27
27
|
)
|
|
28
28
|
ORM_BUCKET_RESULT_PREFIX = "orm_bucket_result"
|
|
29
|
+
ORM_BUCKET_ROW_RESULT_PREFIX = "orm_bucket_row_result"
|
|
29
30
|
BUCKET_INDEX_PREFIX = "bucket_index"
|
|
30
31
|
DEFAULT_DEPENDENCY_CACHE_PUBLISH_BATCH_SIZE = 1000
|
|
31
32
|
logger = get_logger("cache.run_context")
|
|
@@ -191,9 +192,18 @@ class CalculationRunContext:
|
|
|
191
192
|
"""Store an ORM bucket result for the active run."""
|
|
192
193
|
self.set((ORM_BUCKET_RESULT_PREFIX, key), value)
|
|
193
194
|
|
|
195
|
+
def get_orm_bucket_rows(self, key: Hashable) -> object:
|
|
196
|
+
"""Return cached ORM bucket rows for key, or None when absent."""
|
|
197
|
+
return self.get((ORM_BUCKET_ROW_RESULT_PREFIX, key))
|
|
198
|
+
|
|
199
|
+
def set_orm_bucket_rows(self, key: Hashable, value: object) -> None:
|
|
200
|
+
"""Store ORM bucket rows for the active run."""
|
|
201
|
+
self.set((ORM_BUCKET_ROW_RESULT_PREFIX, key), value)
|
|
202
|
+
|
|
194
203
|
def clear_orm_bucket_results(self) -> None:
|
|
195
204
|
"""Discard all run-scoped ORM bucket result entries."""
|
|
196
205
|
self.discard_prefix((ORM_BUCKET_RESULT_PREFIX,))
|
|
206
|
+
self.discard_prefix((ORM_BUCKET_ROW_RESULT_PREFIX,))
|
|
197
207
|
|
|
198
208
|
def _bucket_index_cache_key(
|
|
199
209
|
self,
|