GeneralManager 0.68.3__tar.gz → 0.69.1__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.68.3/src/GeneralManager.egg-info → generalmanager-0.69.1}/PKG-INFO +1 -1
- {generalmanager-0.68.3 → generalmanager-0.69.1}/pyproject.toml +1 -1
- {generalmanager-0.68.3 → generalmanager-0.69.1/src/GeneralManager.egg-info}/PKG-INFO +1 -1
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/graphql.py +37 -29
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/remote_invalidation.py +25 -17
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/cache/data_change_context.py +77 -6
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/cache/dependency_index.py +32 -21
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/cache/run_context.py +127 -7
- generalmanager-0.69.1/src/general_manager/cache/run_context_lru.py +793 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/cache/signals.py +88 -14
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/invalidation.py +50 -44
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/workflow/signal_bridge.py +22 -13
- generalmanager-0.68.3/src/general_manager/cache/run_context_lru.py +0 -460
- {generalmanager-0.68.3 → generalmanager-0.69.1}/LICENSE +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/README.md +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/setup.cfg +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/GeneralManager.egg-info/SOURCES.txt +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/GeneralManager.egg-info/requires.txt +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/GeneralManager.egg-info/top_level.txt +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/_types/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/_types/api.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/_types/bucket.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/_types/cache.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/_types/dataframes.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/_types/factory.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/_types/general_manager.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/_types/interface.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/_types/manager.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/_types/measurement.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/_types/permission.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/_types/rule.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/_types/search.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/_types/utils.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/graphql_as_of.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/graphql_errors.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/graphql_mutations.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/graphql_prefetch.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/graphql_relations.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/graphql_resolvers.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/graphql_search.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/graphql_subscriptions.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/graphql_view.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/graphql_warmup.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/graphql_warmup_registry.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/graphql_warmup_tasks.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/mutation.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/notification_batching.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/property.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/registry.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/remote_api.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/remote_invalidation_client.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/apps.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/as_of.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/bootstrap.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/bucket/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/bucket/base_bucket.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/bucket/calculation_bucket.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/bucket/database_bucket.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/bucket/group_bucket.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/bucket/indexing.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/bucket/request_bucket.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/cache/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/cache/cache_decorator.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/cache/cache_tracker.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/cache/dependency_cache.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/cache/dependency_matching.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/cache/dependency_publish.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/cache/dependency_shards.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/cache/model_dependency_collector.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/audit.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/bootstrap.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/checks.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/consumer.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/errors.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/__main__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/baseline.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/datasets/basic_queries.yaml +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/datasets/demo_readiness.yaml +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/datasets/edge_cases.yaml +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/datasets/follow_ups.yaml +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/datasets/large_schema.yaml +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/datasets/multi_hop.yaml +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/diagnostics.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/fingerprints.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/fixtures.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/judges/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/judges/answer_quality.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/judges/contract.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/judges/result_accuracy.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/judges/tool_sequence.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/readiness.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/runner.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/evals/traces.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/grounding.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/models.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/providers/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/providers/_shared.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/providers/anthropic.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/providers/base.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/providers/google.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/providers/ollama.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/providers/openai.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/rate_limits.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/routing.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/schema_index.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/settings.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/signals.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/system_prompt.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/tool_metadata.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/tools.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/chat/views.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/conf.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/dataframes/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/dataframes/measurements.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/factory/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/factory/auto_factory.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/factory/factories.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/factory/factory_methods.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/base_interface.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/bundles/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/bundles/calculation.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/bundles/database.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/bundles/remote_manager.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/bundles/request.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/base.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/builtin.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/configuration.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/core/observability.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/core/utils.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/exceptions.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/factory.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/orm/history.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/orm/mutations.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/orm/support.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/read_only/_types.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/registry.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/infrastructure/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/interfaces/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/interfaces/calculation.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/interfaces/database.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/interfaces/existing_model.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/interfaces/read_only.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/interfaces/request.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/manifests/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/manifests/capability_builder.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/manifests/capability_models.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/orm_interface.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/requests.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/utils/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/utils/errors.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/utils/history.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/interface/utils/models.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/logging.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/management/commands/chat_cleanup.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/management/commands/cleanup_upload_intents.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/management/commands/graphql_warmup.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/management/commands/graphql_warmup_refresh_due.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/management/commands/search_index.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/management/commands/search_reconcile.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/management/commands/shell.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/manager/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/manager/general_manager.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/manager/group_manager.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/manager/input.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/manager/meta.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/measurement/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/measurement/measurement.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/measurement/measurement_field.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/metrics/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/metrics/graphql.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/migrations/0001_initial.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/migrations/0004_chat_persistence_models.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/migrations/0004_search_index_state.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/migrations/0005_merge_chat_search.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/migrations/0006_chat_pending_confirmation_scoped_ids.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/migrations/0007_upload_intent.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/migrations/0008_upload_finalization_versions.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/migrations/0009_upload_cleanup_state.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/migrations/0010_chat_pending_confirmation_portable_uniqueness.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/migrations/0011_search_index_state_dirty_generation.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/migrations/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/models.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/permission/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/permission/audit.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/permission/base_permission.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/permission/graphql_capabilities.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/permission/manager_based_permission.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/permission/mutation_permission.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/permission/permission_checks.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/permission/permission_data_manager.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/permission/utils.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/public_api_registry.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/py.typed +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/rule/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/rule/handler.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/rule/rule.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/async_tasks.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/backend.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/backend_registry.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/backends/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/backends/dev.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/backends/meilisearch.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/backends/opensearch.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/backends/typesense.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/checks.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/config.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/indexer.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/m2m_invalidation.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/models.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/reconciliation.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/registry.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/tasks.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/search/utils.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/seeding/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/seeding/manager_landscape.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/adapters.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/checks.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/config.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/errors.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/finalization.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/graphql.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/graphql_types.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/metrics.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/models.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/public.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/s3.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/services.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/tokens.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/types.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/urls.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/uploads/views.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/utils/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/utils/args_to_kwargs.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/utils/filter_parser.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/utils/format_string.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/utils/json_encoder.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/utils/make_cache_key.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/utils/none_to_zero.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/utils/path_mapping.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/utils/public_api.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/utils/testing.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/utils/type_checks.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/workflow/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/workflow/actions.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/workflow/backend_registry.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/workflow/backends/__init__.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/workflow/backends/celery.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/workflow/backends/local.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/workflow/backends/n8n.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/workflow/config.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/workflow/engine.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/workflow/event_registry.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/workflow/events.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/workflow/models.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/workflow/tasks.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/workflow/telemetry.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/tests/test_settings.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/tests/test_urls.py +0 -0
- {generalmanager-0.68.3 → generalmanager-0.69.1}/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.69.1
|
|
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.69.1"
|
|
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.69.1
|
|
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
|
|
@@ -6,6 +6,7 @@ import asyncio
|
|
|
6
6
|
from contextlib import asynccontextmanager
|
|
7
7
|
from copy import deepcopy
|
|
8
8
|
import re
|
|
9
|
+
from time import perf_counter
|
|
9
10
|
from types import UnionType
|
|
10
11
|
from typing import (
|
|
11
12
|
AsyncIterator,
|
|
@@ -37,6 +38,7 @@ from general_manager.bucket.group_bucket import GroupBucket
|
|
|
37
38
|
from general_manager.cache.dependency_index import (
|
|
38
39
|
Dependency,
|
|
39
40
|
)
|
|
41
|
+
from general_manager.cache.data_change_context import record_data_change_phase
|
|
40
42
|
from general_manager.cache.signals import post_data_change
|
|
41
43
|
from general_manager.conf import get_setting
|
|
42
44
|
from general_manager.interface.base_interface import InterfaceBase
|
|
@@ -1784,38 +1786,44 @@ class GraphQL:
|
|
|
1784
1786
|
instance (GeneralManager | None): The GeneralManager instance that changed.
|
|
1785
1787
|
action (str): A string describing the change action (e.g., "created", "updated", "deleted").
|
|
1786
1788
|
"""
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1789
|
+
started = perf_counter()
|
|
1790
|
+
try:
|
|
1791
|
+
event_instance = instance if instance is not None else previous_instance
|
|
1792
|
+
if event_instance is None or not isinstance(event_instance, GeneralManager):
|
|
1793
|
+
return
|
|
1790
1794
|
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
+
if isinstance(sender, type) and issubclass(sender, GeneralManager):
|
|
1796
|
+
manager_class: type[GeneralManager] = sender
|
|
1797
|
+
else:
|
|
1798
|
+
manager_class = event_instance.__class__
|
|
1795
1799
|
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1800
|
+
if manager_class.__name__ not in cls.manager_registry:
|
|
1801
|
+
logger.debug(
|
|
1802
|
+
"skipping subscription event for unregistered manager",
|
|
1803
|
+
context={
|
|
1804
|
+
"manager": manager_class.__name__,
|
|
1805
|
+
"action": action,
|
|
1806
|
+
},
|
|
1807
|
+
)
|
|
1808
|
+
return
|
|
1805
1809
|
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1810
|
+
event_identification = deepcopy(
|
|
1811
|
+
identification
|
|
1812
|
+
if identification is not None
|
|
1813
|
+
else event_instance.identification
|
|
1814
|
+
)
|
|
1815
|
+
transaction.on_commit(
|
|
1816
|
+
lambda: cls._publish_data_change(
|
|
1817
|
+
manager_class,
|
|
1818
|
+
action,
|
|
1819
|
+
event_identification,
|
|
1820
|
+
),
|
|
1821
|
+
using=database_alias,
|
|
1822
|
+
)
|
|
1823
|
+
finally:
|
|
1824
|
+
record_data_change_phase(
|
|
1825
|
+
"subscription", perf_counter() - started, database_alias
|
|
1826
|
+
)
|
|
1819
1827
|
|
|
1820
1828
|
@classmethod
|
|
1821
1829
|
def _publish_data_change(
|
{generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/api/remote_invalidation.py
RENAMED
|
@@ -8,6 +8,7 @@ from datetime import date, datetime
|
|
|
8
8
|
import json
|
|
9
9
|
from importlib import import_module
|
|
10
10
|
import re
|
|
11
|
+
from time import perf_counter
|
|
11
12
|
from uuid import UUID, uuid4
|
|
12
13
|
from collections.abc import Callable, Coroutine, Mapping, MutableSequence, Sequence
|
|
13
14
|
from types import ModuleType
|
|
@@ -25,6 +26,7 @@ from general_manager.api.remote_api import (
|
|
|
25
26
|
get_remote_api_config,
|
|
26
27
|
)
|
|
27
28
|
from general_manager.api.notification_batching import _queue_notification
|
|
29
|
+
from general_manager.cache.data_change_context import record_data_change_phase
|
|
28
30
|
from general_manager.cache.signals import post_data_change
|
|
29
31
|
from general_manager.logging import get_logger
|
|
30
32
|
|
|
@@ -200,24 +202,30 @@ def emit_remote_invalidation(
|
|
|
200
202
|
originating database alias and delegates channel lookup, batching, payload
|
|
201
203
|
creation, and delivery to `_publish_remote_invalidation` after commit.
|
|
202
204
|
"""
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
205
|
+
started = perf_counter()
|
|
206
|
+
try:
|
|
207
|
+
config = get_remote_api_config(sender)
|
|
208
|
+
if config is None or not config.websocket_invalidation:
|
|
209
|
+
return
|
|
206
210
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
211
|
+
event_identification = identification
|
|
212
|
+
if event_identification is None and instance is not None:
|
|
213
|
+
event_identification = dict(instance.identification)
|
|
214
|
+
captured_identification = (
|
|
215
|
+
deepcopy(event_identification) if event_identification is not None else None
|
|
216
|
+
)
|
|
217
|
+
transaction.on_commit(
|
|
218
|
+
lambda: _publish_remote_invalidation(
|
|
219
|
+
config,
|
|
220
|
+
action,
|
|
221
|
+
captured_identification,
|
|
222
|
+
),
|
|
223
|
+
using=database_alias,
|
|
224
|
+
)
|
|
225
|
+
finally:
|
|
226
|
+
record_data_change_phase(
|
|
227
|
+
"subscription", perf_counter() - started, database_alias
|
|
228
|
+
)
|
|
221
229
|
|
|
222
230
|
|
|
223
231
|
post_data_change.connect(emit_remote_invalidation, weak=False)
|
{generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/cache/data_change_context.py
RENAMED
|
@@ -5,17 +5,45 @@ from __future__ import annotations
|
|
|
5
5
|
from collections.abc import Iterator
|
|
6
6
|
from contextlib import contextmanager
|
|
7
7
|
from contextvars import ContextVar
|
|
8
|
-
from dataclasses import dataclass
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from math import isfinite
|
|
10
|
+
from typing import Literal
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class DataChangeTransactionContext:
|
|
15
|
+
"""Mutable state owned by one framework-managed data-change transaction."""
|
|
16
|
+
|
|
17
|
+
database_alias: str
|
|
18
|
+
caller_in_atomic_block: bool
|
|
19
|
+
changed_classes: set[str] = field(default_factory=set)
|
|
20
|
+
metadata: dict[str, object] = field(default_factory=dict)
|
|
21
|
+
phase_seconds: dict[str, float] = field(default_factory=dict)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class DataChangeTransactionScope:
|
|
26
|
+
"""Public view of a context-manager entry's transaction state."""
|
|
27
|
+
|
|
28
|
+
transaction: DataChangeTransactionContext
|
|
29
|
+
is_outermost: bool
|
|
9
30
|
|
|
10
31
|
|
|
11
32
|
@dataclass
|
|
12
33
|
class _DataChangeTransaction:
|
|
13
34
|
"""Live marker for one framework-owned transaction envelope."""
|
|
14
35
|
|
|
15
|
-
|
|
16
|
-
caller_in_atomic_block: bool
|
|
36
|
+
context: DataChangeTransactionContext
|
|
17
37
|
active: bool = True
|
|
18
38
|
|
|
39
|
+
@property
|
|
40
|
+
def database_alias(self) -> str:
|
|
41
|
+
return self.context.database_alias
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def caller_in_atomic_block(self) -> bool:
|
|
45
|
+
return self.context.caller_in_atomic_block
|
|
46
|
+
|
|
19
47
|
|
|
20
48
|
_owned_transactions: ContextVar[tuple[_DataChangeTransaction, ...]] = ContextVar(
|
|
21
49
|
"owned_data_change_transactions",
|
|
@@ -34,25 +62,68 @@ def _current_owner(database_alias: str) -> _DataChangeTransaction | None:
|
|
|
34
62
|
return None
|
|
35
63
|
|
|
36
64
|
|
|
65
|
+
def current_data_change_transaction(
|
|
66
|
+
database_alias: str,
|
|
67
|
+
) -> DataChangeTransactionContext | None:
|
|
68
|
+
"""Return the live framework-owned context for a database alias, if any."""
|
|
69
|
+
owner = _current_owner(database_alias)
|
|
70
|
+
return owner.context if owner is not None else None
|
|
71
|
+
|
|
72
|
+
|
|
37
73
|
@contextmanager
|
|
38
74
|
def own_data_change_transaction(
|
|
39
75
|
database_alias: str,
|
|
40
76
|
*,
|
|
41
77
|
caller_in_atomic_block: bool,
|
|
42
|
-
) -> Iterator[
|
|
78
|
+
) -> Iterator[DataChangeTransactionScope]:
|
|
43
79
|
"""Record a live framework envelope without inspecting Django internals."""
|
|
44
|
-
|
|
80
|
+
existing_owner = _current_owner(database_alias)
|
|
81
|
+
if existing_owner is not None:
|
|
82
|
+
yield DataChangeTransactionScope(
|
|
83
|
+
transaction=existing_owner.context,
|
|
84
|
+
is_outermost=False,
|
|
85
|
+
)
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
context = DataChangeTransactionContext(
|
|
45
89
|
database_alias=database_alias,
|
|
46
90
|
caller_in_atomic_block=caller_in_atomic_block,
|
|
47
91
|
)
|
|
92
|
+
owner = _DataChangeTransaction(
|
|
93
|
+
context=context,
|
|
94
|
+
)
|
|
48
95
|
token = _owned_transactions.set((*_owned_transactions.get(), owner))
|
|
49
96
|
try:
|
|
50
|
-
yield
|
|
97
|
+
yield DataChangeTransactionScope(transaction=context, is_outermost=True)
|
|
51
98
|
finally:
|
|
52
99
|
owner.active = False
|
|
53
100
|
_owned_transactions.reset(token)
|
|
54
101
|
|
|
55
102
|
|
|
103
|
+
def register_data_change_class(class_name: str, database_alias: str) -> bool:
|
|
104
|
+
"""Record a changed class when the framework owns the live transaction."""
|
|
105
|
+
context = current_data_change_transaction(database_alias)
|
|
106
|
+
if context is None or context.caller_in_atomic_block:
|
|
107
|
+
return False
|
|
108
|
+
context.changed_classes.add(class_name)
|
|
109
|
+
return True
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def record_data_change_phase(
|
|
113
|
+
phase: Literal["database", "invalidation", "subscription", "search", "workflow"],
|
|
114
|
+
duration_seconds: float,
|
|
115
|
+
database_alias: str,
|
|
116
|
+
) -> None:
|
|
117
|
+
"""Accumulate a non-negative duration for a phase in the live context."""
|
|
118
|
+
if not isfinite(duration_seconds):
|
|
119
|
+
return
|
|
120
|
+
context = current_data_change_transaction(database_alias)
|
|
121
|
+
if context is not None:
|
|
122
|
+
context.phase_seconds[phase] = context.phase_seconds.get(phase, 0.0) + max(
|
|
123
|
+
duration_seconds, 0.0
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
56
127
|
@contextmanager
|
|
57
128
|
def authorize_data_change_operation(database_alias: str) -> Iterator[None]:
|
|
58
129
|
"""Authorize only the decorated manager method to reuse its envelope."""
|
{generalmanager-0.68.3 → generalmanager-0.69.1}/src/general_manager/cache/dependency_index.py
RENAMED
|
@@ -8,9 +8,11 @@ import time
|
|
|
8
8
|
import uuid
|
|
9
9
|
from contextvars import ContextVar
|
|
10
10
|
from datetime import date, datetime
|
|
11
|
+
from time import perf_counter
|
|
11
12
|
from typing import TYPE_CHECKING, Callable, Iterable, Literal, Tuple, Type, cast
|
|
12
13
|
|
|
13
14
|
from django.core.cache import cache
|
|
15
|
+
from django.db import DEFAULT_DB_ALIAS
|
|
14
16
|
from django.dispatch import receiver
|
|
15
17
|
|
|
16
18
|
from general_manager.cache.dependency_matching import (
|
|
@@ -34,6 +36,7 @@ from general_manager.cache.dependency_shards import (
|
|
|
34
36
|
reverse_memberships,
|
|
35
37
|
tracked_lookup_names,
|
|
36
38
|
)
|
|
39
|
+
from general_manager.cache.data_change_context import record_data_change_phase
|
|
37
40
|
from general_manager.cache.signals import post_data_change, pre_data_change
|
|
38
41
|
from general_manager.logging import get_logger
|
|
39
42
|
|
|
@@ -1384,6 +1387,7 @@ def generic_cache_invalidation(
|
|
|
1384
1387
|
sender: type[GeneralManager],
|
|
1385
1388
|
instance: GeneralManager,
|
|
1386
1389
|
old_relevant_values: dict[str, object],
|
|
1390
|
+
database_alias: str = DEFAULT_DB_ALIAS,
|
|
1387
1391
|
**kwargs: object,
|
|
1388
1392
|
) -> None:
|
|
1389
1393
|
"""
|
|
@@ -1395,27 +1399,34 @@ def generic_cache_invalidation(
|
|
|
1395
1399
|
sender (type[GeneralManager]): Manager class that emitted the signal.
|
|
1396
1400
|
instance (GeneralManager): The manager instance that was changed.
|
|
1397
1401
|
old_relevant_values (dict[str, object]): Mapping of lookup paths (joined by "__") to their values as captured before the change; used to compare old vs. new values for invalidation decisions.
|
|
1402
|
+
database_alias (str): Database alias used to attribute invalidation timing.
|
|
1398
1403
|
"""
|
|
1399
|
-
|
|
1400
|
-
invalidated_cache_keys: set[str] = set()
|
|
1401
|
-
lock_token = acquire_lock_with_retry("generic_cache_invalidation")
|
|
1404
|
+
started = perf_counter()
|
|
1402
1405
|
try:
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1406
|
+
manager_name = sender.__name__
|
|
1407
|
+
invalidated_cache_keys: set[str] = set()
|
|
1408
|
+
lock_token = acquire_lock_with_retry("generic_cache_invalidation")
|
|
1409
|
+
try:
|
|
1410
|
+
if legacy_dependency_index_exists():
|
|
1411
|
+
idx = get_full_index()
|
|
1412
|
+
invalidated_cache_keys = _generic_cache_invalidation_locked(
|
|
1413
|
+
idx,
|
|
1414
|
+
manager_name,
|
|
1415
|
+
instance,
|
|
1416
|
+
old_relevant_values,
|
|
1417
|
+
)
|
|
1418
|
+
set_full_index(idx)
|
|
1419
|
+
else:
|
|
1420
|
+
invalidated_cache_keys = _generic_cache_invalidation_from_shards(
|
|
1421
|
+
manager_name,
|
|
1422
|
+
instance,
|
|
1423
|
+
old_relevant_values,
|
|
1424
|
+
)
|
|
1425
|
+
finally:
|
|
1426
|
+
release_lock(lock_token)
|
|
1427
|
+
if invalidated_cache_keys:
|
|
1428
|
+
record_invalidated_cache_keys_for_graphql_rewarm(invalidated_cache_keys)
|
|
1418
1429
|
finally:
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1430
|
+
record_data_change_phase(
|
|
1431
|
+
"invalidation", perf_counter() - started, database_alias
|
|
1432
|
+
)
|
|
@@ -6,6 +6,7 @@ from collections import defaultdict
|
|
|
6
6
|
from collections.abc import Callable, Hashable, Iterable, Mapping
|
|
7
7
|
from contextvars import ContextVar, Token
|
|
8
8
|
from dataclasses import dataclass
|
|
9
|
+
from threading import RLock, get_ident
|
|
9
10
|
from types import TracebackType
|
|
10
11
|
from typing import TYPE_CHECKING, Optional, TypeVar, cast
|
|
11
12
|
|
|
@@ -43,8 +44,10 @@ ORM_BUCKET_EXISTS_PREFIX = "orm_bucket_exists"
|
|
|
43
44
|
BUCKET_INDEX_PREFIX = "bucket_index"
|
|
44
45
|
TRUSTED_ORM_MANAGER_PREFIX = "trusted_orm_manager"
|
|
45
46
|
DEFAULT_DEPENDENCY_CACHE_PUBLISH_BATCH_SIZE = 1000
|
|
47
|
+
RUN_CONTEXT_TOUCH_BATCH_SIZE = 64
|
|
46
48
|
logger = get_logger("cache.run_context")
|
|
47
49
|
OrmModelRowKey = tuple[Hashable, Hashable | None]
|
|
50
|
+
PendingRunCacheTouch = tuple[RunCacheNamespace, Hashable]
|
|
48
51
|
|
|
49
52
|
|
|
50
53
|
@dataclass(frozen=True)
|
|
@@ -104,9 +107,16 @@ class CalculationRunContext:
|
|
|
104
107
|
] = {}
|
|
105
108
|
self._dependency_cache_publish_batch_size = dependency_cache_publish_batch_size
|
|
106
109
|
self._tokens: list[Token[CalculationRunContext | None]] = []
|
|
110
|
+
self._run_cache_touch_lock = RLock()
|
|
111
|
+
self._pending_run_cache_touches: dict[PendingRunCacheTouch, None] = {}
|
|
112
|
+
self._last_pending_run_cache_touch: PendingRunCacheTouch | None = None
|
|
113
|
+
self._run_cache_touch_count = 0
|
|
114
|
+
self._run_cache_touch_thread_id: int | None = None
|
|
115
|
+
max_bytes = resolve_run_context_cache_max_bytes()
|
|
116
|
+
self._run_cache_budget_enabled = max_bytes is not None
|
|
107
117
|
run_context_cache_budget.register(
|
|
108
118
|
self,
|
|
109
|
-
|
|
119
|
+
max_bytes,
|
|
110
120
|
)
|
|
111
121
|
|
|
112
122
|
def _iter_run_cache_entries(
|
|
@@ -119,31 +129,45 @@ class CalculationRunContext:
|
|
|
119
129
|
if key not in self._dependency_cache_pending_publications:
|
|
120
130
|
yield "dependency_hits", key, hit
|
|
121
131
|
|
|
132
|
+
def _set_run_cache_budget_enabled(self, enabled: bool) -> None:
|
|
133
|
+
"""Refresh the owner hint after a coordinator transition."""
|
|
134
|
+
with self._run_cache_touch_lock:
|
|
135
|
+
self._run_cache_budget_enabled = enabled
|
|
136
|
+
if not enabled:
|
|
137
|
+
self._pending_run_cache_touches.clear()
|
|
138
|
+
self._last_pending_run_cache_touch = None
|
|
139
|
+
self._run_cache_touch_count = 0
|
|
140
|
+
|
|
122
141
|
def _evict_run_cache_entry(
|
|
123
142
|
self,
|
|
124
143
|
namespace: RunCacheNamespace,
|
|
125
144
|
key: Hashable,
|
|
126
145
|
) -> None:
|
|
127
146
|
"""Remove one entry selected by the process-wide coordinator."""
|
|
147
|
+
self._discard_run_cache_touch((namespace, key))
|
|
128
148
|
if namespace == "values":
|
|
129
149
|
self._values.pop(key, None)
|
|
130
150
|
elif key not in self._dependency_cache_pending_publications:
|
|
131
151
|
self._dependency_cache_hits.pop(cast(str, key), None)
|
|
132
152
|
|
|
133
153
|
def _store_run_value(self, scoped_key: Hashable, value: object) -> None:
|
|
154
|
+
self._flush_run_cache_touches()
|
|
134
155
|
self._values[scoped_key] = value
|
|
135
156
|
run_context_cache_budget.track(self, "values", scoped_key, value)
|
|
136
157
|
|
|
137
158
|
def _remove_run_value(self, scoped_key: Hashable) -> None:
|
|
159
|
+
self._discard_run_cache_touch(("values", scoped_key))
|
|
138
160
|
if scoped_key in self._values:
|
|
139
161
|
del self._values[scoped_key]
|
|
140
162
|
run_context_cache_budget.remove(self, "values", scoped_key)
|
|
141
163
|
|
|
142
164
|
def _refresh_run_value(self, scoped_key: Hashable) -> None:
|
|
165
|
+
self._discard_run_cache_touch(("values", scoped_key))
|
|
143
166
|
try:
|
|
144
167
|
value = self._values[scoped_key]
|
|
145
168
|
except KeyError:
|
|
146
169
|
return
|
|
170
|
+
self._flush_run_cache_touches()
|
|
147
171
|
run_context_cache_budget.refresh(self, "values", scoped_key, value)
|
|
148
172
|
|
|
149
173
|
def _store_dependency_cache_hit(
|
|
@@ -151,26 +175,109 @@ class CalculationRunContext:
|
|
|
151
175
|
key: str,
|
|
152
176
|
hit: DependencyCacheHit,
|
|
153
177
|
) -> None:
|
|
178
|
+
is_pending_publication = key in self._dependency_cache_pending_publications
|
|
179
|
+
if not is_pending_publication:
|
|
180
|
+
self._flush_run_cache_touches()
|
|
154
181
|
self._dependency_cache_hits[key] = hit
|
|
155
|
-
if
|
|
182
|
+
if is_pending_publication:
|
|
183
|
+
self._discard_run_cache_touch(("dependency_hits", key))
|
|
156
184
|
run_context_cache_budget.remove(self, "dependency_hits", key)
|
|
157
185
|
else:
|
|
158
186
|
run_context_cache_budget.track(self, "dependency_hits", key, hit)
|
|
159
187
|
|
|
160
188
|
def _remove_dependency_cache_hit(self, key: str) -> None:
|
|
189
|
+
self._discard_run_cache_touch(("dependency_hits", key))
|
|
161
190
|
if key in self._dependency_cache_hits:
|
|
162
191
|
del self._dependency_cache_hits[key]
|
|
163
192
|
run_context_cache_budget.remove(self, "dependency_hits", key)
|
|
164
193
|
|
|
165
194
|
def _refresh_dependency_cache_hit(self, key: str) -> None:
|
|
195
|
+
self._discard_run_cache_touch(("dependency_hits", key))
|
|
166
196
|
if key in self._dependency_cache_pending_publications:
|
|
167
197
|
return
|
|
168
198
|
try:
|
|
169
199
|
hit = self._dependency_cache_hits[key]
|
|
170
200
|
except KeyError:
|
|
171
201
|
return
|
|
202
|
+
self._flush_run_cache_touches()
|
|
172
203
|
run_context_cache_budget.refresh(self, "dependency_hits", key, hit)
|
|
173
204
|
|
|
205
|
+
def _queue_run_cache_touch(
|
|
206
|
+
self,
|
|
207
|
+
namespace: RunCacheNamespace,
|
|
208
|
+
key: Hashable,
|
|
209
|
+
) -> None:
|
|
210
|
+
pending_key = (namespace, key)
|
|
211
|
+
with self._run_cache_touch_lock:
|
|
212
|
+
if not self._run_cache_budget_enabled:
|
|
213
|
+
return
|
|
214
|
+
if pending_key != self._last_pending_run_cache_touch:
|
|
215
|
+
self._pending_run_cache_touches.pop(pending_key, None)
|
|
216
|
+
self._pending_run_cache_touches[pending_key] = None
|
|
217
|
+
self._last_pending_run_cache_touch = pending_key
|
|
218
|
+
self._queue_repeated_run_cache_touch(pending_key)
|
|
219
|
+
|
|
220
|
+
def _queue_repeated_run_cache_touch(
|
|
221
|
+
self,
|
|
222
|
+
pending_key: PendingRunCacheTouch,
|
|
223
|
+
) -> bool:
|
|
224
|
+
touch_immediately = False
|
|
225
|
+
touches_to_flush: tuple[PendingRunCacheTouch, ...] = ()
|
|
226
|
+
with self._run_cache_touch_lock:
|
|
227
|
+
if not self._run_cache_budget_enabled:
|
|
228
|
+
return False
|
|
229
|
+
if self._run_cache_touch_thread_id != get_ident():
|
|
230
|
+
touch_immediately = True
|
|
231
|
+
elif pending_key != self._last_pending_run_cache_touch:
|
|
232
|
+
return False
|
|
233
|
+
else:
|
|
234
|
+
count = self._run_cache_touch_count + 1
|
|
235
|
+
if count < RUN_CONTEXT_TOUCH_BATCH_SIZE:
|
|
236
|
+
self._run_cache_touch_count = count
|
|
237
|
+
return True
|
|
238
|
+
touches_to_flush = self._take_run_cache_touches_locked()
|
|
239
|
+
if touch_immediately:
|
|
240
|
+
run_context_cache_budget.touch(self, *pending_key)
|
|
241
|
+
return True
|
|
242
|
+
run_context_cache_budget.touch_many(self, touches_to_flush)
|
|
243
|
+
return True
|
|
244
|
+
|
|
245
|
+
def _touch_run_cache_entry(
|
|
246
|
+
self,
|
|
247
|
+
namespace: RunCacheNamespace,
|
|
248
|
+
key: Hashable,
|
|
249
|
+
) -> None:
|
|
250
|
+
pending_key: PendingRunCacheTouch = (namespace, key)
|
|
251
|
+
if not self._queue_repeated_run_cache_touch(pending_key):
|
|
252
|
+
self._queue_run_cache_touch(namespace, key)
|
|
253
|
+
|
|
254
|
+
def _discard_run_cache_touch(self, pending_key: PendingRunCacheTouch) -> None:
|
|
255
|
+
with self._run_cache_touch_lock:
|
|
256
|
+
self._pending_run_cache_touches.pop(pending_key, None)
|
|
257
|
+
if pending_key == self._last_pending_run_cache_touch:
|
|
258
|
+
self._last_pending_run_cache_touch = next(
|
|
259
|
+
reversed(self._pending_run_cache_touches),
|
|
260
|
+
None,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
def _take_run_cache_touches_locked(
|
|
264
|
+
self,
|
|
265
|
+
) -> tuple[PendingRunCacheTouch, ...]:
|
|
266
|
+
touches = tuple(self._pending_run_cache_touches)
|
|
267
|
+
self._pending_run_cache_touches.clear()
|
|
268
|
+
self._last_pending_run_cache_touch = None
|
|
269
|
+
self._run_cache_touch_count = 0
|
|
270
|
+
return touches
|
|
271
|
+
|
|
272
|
+
def _flush_run_cache_touches(self) -> None:
|
|
273
|
+
with self._run_cache_touch_lock:
|
|
274
|
+
if not self._pending_run_cache_touches:
|
|
275
|
+
self._last_pending_run_cache_touch = None
|
|
276
|
+
self._run_cache_touch_count = 0
|
|
277
|
+
return
|
|
278
|
+
touches = self._take_run_cache_touches_locked()
|
|
279
|
+
run_context_cache_budget.touch_many(self, touches)
|
|
280
|
+
|
|
174
281
|
@staticmethod
|
|
175
282
|
def _scoped_key(key: Hashable) -> Hashable:
|
|
176
283
|
"""Namespace a run value by the active historical snapshot."""
|
|
@@ -182,9 +289,16 @@ class CalculationRunContext:
|
|
|
182
289
|
def __enter__(self) -> "CalculationRunContext":
|
|
183
290
|
"""Activate this context and return it for the `with` block."""
|
|
184
291
|
if not self._tokens:
|
|
292
|
+
max_bytes = resolve_run_context_cache_max_bytes()
|
|
293
|
+
with self._run_cache_touch_lock:
|
|
294
|
+
self._pending_run_cache_touches.clear()
|
|
295
|
+
self._last_pending_run_cache_touch = None
|
|
296
|
+
self._run_cache_touch_count = 0
|
|
297
|
+
self._run_cache_touch_thread_id = get_ident()
|
|
298
|
+
self._run_cache_budget_enabled = max_bytes is not None
|
|
185
299
|
run_context_cache_budget.register(
|
|
186
300
|
self,
|
|
187
|
-
|
|
301
|
+
max_bytes,
|
|
188
302
|
)
|
|
189
303
|
self._tokens.append(_active_context.set(self))
|
|
190
304
|
return self
|
|
@@ -219,6 +333,12 @@ class CalculationRunContext:
|
|
|
219
333
|
|
|
220
334
|
token = self._tokens.pop()
|
|
221
335
|
is_outermost_exit = not self._tokens
|
|
336
|
+
if is_outermost_exit:
|
|
337
|
+
with self._run_cache_touch_lock:
|
|
338
|
+
self._pending_run_cache_touches.clear()
|
|
339
|
+
self._last_pending_run_cache_touch = None
|
|
340
|
+
self._run_cache_touch_count = 0
|
|
341
|
+
self._run_cache_touch_thread_id = None
|
|
222
342
|
try:
|
|
223
343
|
if is_outermost_exit:
|
|
224
344
|
if exc_type is None:
|
|
@@ -253,7 +373,7 @@ class CalculationRunContext:
|
|
|
253
373
|
value = loader()
|
|
254
374
|
self._store_run_value(scoped_key, value)
|
|
255
375
|
return value
|
|
256
|
-
|
|
376
|
+
self._touch_run_cache_entry("values", scoped_key)
|
|
257
377
|
return cast(T, value)
|
|
258
378
|
|
|
259
379
|
def get(self, key: Hashable, default: object = None) -> object:
|
|
@@ -263,7 +383,7 @@ class CalculationRunContext:
|
|
|
263
383
|
value = self._values[scoped_key]
|
|
264
384
|
except KeyError:
|
|
265
385
|
return default
|
|
266
|
-
|
|
386
|
+
self._touch_run_cache_entry("values", scoped_key)
|
|
267
387
|
return value
|
|
268
388
|
|
|
269
389
|
def set(self, key: Hashable, value: object) -> None:
|
|
@@ -295,7 +415,7 @@ class CalculationRunContext:
|
|
|
295
415
|
except KeyError:
|
|
296
416
|
return default
|
|
297
417
|
if key not in self._dependency_cache_pending_publications:
|
|
298
|
-
|
|
418
|
+
self._touch_run_cache_entry("dependency_hits", key)
|
|
299
419
|
return hit
|
|
300
420
|
|
|
301
421
|
def buffer_dependency_cache_publication(
|
|
@@ -718,7 +838,7 @@ class CalculationRunContext:
|
|
|
718
838
|
scoped_key = self._scoped_key(key)
|
|
719
839
|
if scoped_key not in self._values:
|
|
720
840
|
return False
|
|
721
|
-
|
|
841
|
+
self._touch_run_cache_entry("values", scoped_key)
|
|
722
842
|
return True
|
|
723
843
|
|
|
724
844
|
def __contains__(self, key: Hashable) -> bool:
|