tasker-engine 1.0.0
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.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +443 -0
- data/Rakefile +10 -0
- data/app/controllers/tasker/analytics_controller.rb +179 -0
- data/app/controllers/tasker/application_controller.rb +45 -0
- data/app/controllers/tasker/graphql_controller.rb +193 -0
- data/app/controllers/tasker/handlers_controller.rb +217 -0
- data/app/controllers/tasker/health_controller.rb +229 -0
- data/app/controllers/tasker/metrics_controller.rb +111 -0
- data/app/controllers/tasker/page_sort.rb +97 -0
- data/app/controllers/tasker/task_diagrams_controller.rb +30 -0
- data/app/controllers/tasker/tasks_controller.rb +123 -0
- data/app/controllers/tasker/workflow_steps_controller.rb +69 -0
- data/app/graphql/examples/all_tasks.graphql +22 -0
- data/app/graphql/examples/pending_tasks.graphql +23 -0
- data/app/graphql/tasker/graph_ql_types/annotation_type.rb +14 -0
- data/app/graphql/tasker/graph_ql_types/base_argument.rb +9 -0
- data/app/graphql/tasker/graph_ql_types/base_connection.rb +11 -0
- data/app/graphql/tasker/graph_ql_types/base_edge.rb +10 -0
- data/app/graphql/tasker/graph_ql_types/base_enum.rb +9 -0
- data/app/graphql/tasker/graph_ql_types/base_field.rb +10 -0
- data/app/graphql/tasker/graph_ql_types/base_input_object.rb +10 -0
- data/app/graphql/tasker/graph_ql_types/base_interface.rb +14 -0
- data/app/graphql/tasker/graph_ql_types/base_object.rb +10 -0
- data/app/graphql/tasker/graph_ql_types/base_scalar.rb +9 -0
- data/app/graphql/tasker/graph_ql_types/base_union.rb +11 -0
- data/app/graphql/tasker/graph_ql_types/dependent_system_object_map_type.rb +18 -0
- data/app/graphql/tasker/graph_ql_types/dependent_system_type.rb +13 -0
- data/app/graphql/tasker/graph_ql_types/mutation_type.rb +16 -0
- data/app/graphql/tasker/graph_ql_types/named_step_type.rb +16 -0
- data/app/graphql/tasker/graph_ql_types/named_task_type.rb +14 -0
- data/app/graphql/tasker/graph_ql_types/named_tasks_named_step_type.rb +19 -0
- data/app/graphql/tasker/graph_ql_types/node_type.rb +12 -0
- data/app/graphql/tasker/graph_ql_types/query_type.rb +20 -0
- data/app/graphql/tasker/graph_ql_types/task_annotation_type.rb +17 -0
- data/app/graphql/tasker/graph_ql_types/task_interface.rb +17 -0
- data/app/graphql/tasker/graph_ql_types/task_type.rb +26 -0
- data/app/graphql/tasker/graph_ql_types/workflow_step_type.rb +154 -0
- data/app/graphql/tasker/graph_ql_types.rb +42 -0
- data/app/graphql/tasker/mutations/base_mutation.rb +13 -0
- data/app/graphql/tasker/mutations/cancel_step.rb +29 -0
- data/app/graphql/tasker/mutations/cancel_task.rb +29 -0
- data/app/graphql/tasker/mutations/create_task.rb +52 -0
- data/app/graphql/tasker/mutations/update_step.rb +36 -0
- data/app/graphql/tasker/mutations/update_task.rb +41 -0
- data/app/graphql/tasker/queries/all_annotation_types.rb +17 -0
- data/app/graphql/tasker/queries/all_tasks.rb +23 -0
- data/app/graphql/tasker/queries/base_query.rb +9 -0
- data/app/graphql/tasker/queries/helpers.rb +16 -0
- data/app/graphql/tasker/queries/one_step.rb +24 -0
- data/app/graphql/tasker/queries/one_task.rb +18 -0
- data/app/graphql/tasker/queries/tasks_by_annotation.rb +31 -0
- data/app/graphql/tasker/queries/tasks_by_status.rb +30 -0
- data/app/graphql/tasker/tasker_rails_schema.rb +52 -0
- data/app/jobs/tasker/application_job.rb +8 -0
- data/app/jobs/tasker/metrics_export_job.rb +252 -0
- data/app/jobs/tasker/task_runner_job.rb +224 -0
- data/app/models/tasker/annotation_type.rb +26 -0
- data/app/models/tasker/application_record.rb +70 -0
- data/app/models/tasker/dependent_system.rb +26 -0
- data/app/models/tasker/dependent_system_object_map.rb +64 -0
- data/app/models/tasker/diagram/edge.rb +106 -0
- data/app/models/tasker/diagram/flowchart.rb +137 -0
- data/app/models/tasker/diagram/node.rb +99 -0
- data/app/models/tasker/named_step.rb +41 -0
- data/app/models/tasker/named_task.rb +121 -0
- data/app/models/tasker/named_tasks_named_step.rb +82 -0
- data/app/models/tasker/step_dag_relationship.rb +65 -0
- data/app/models/tasker/step_readiness_status.rb +59 -0
- data/app/models/tasker/task.rb +424 -0
- data/app/models/tasker/task_annotation.rb +36 -0
- data/app/models/tasker/task_diagram.rb +332 -0
- data/app/models/tasker/task_execution_context.rb +29 -0
- data/app/models/tasker/task_namespace.rb +41 -0
- data/app/models/tasker/task_transition.rb +235 -0
- data/app/models/tasker/workflow_step.rb +461 -0
- data/app/models/tasker/workflow_step_edge.rb +94 -0
- data/app/models/tasker/workflow_step_transition.rb +434 -0
- data/app/serializers/tasker/annotation_type_serializer.rb +8 -0
- data/app/serializers/tasker/handler_serializer.rb +109 -0
- data/app/serializers/tasker/task_annotation_serializer.rb +32 -0
- data/app/serializers/tasker/task_serializer.rb +168 -0
- data/app/serializers/tasker/workflow_step_serializer.rb +27 -0
- data/app/services/tasker/analytics_service.rb +409 -0
- data/app/views/tasker/task/_diagram.html.erb +32 -0
- data/config/initializers/dry_struct.rb +11 -0
- data/config/initializers/statesman.rb +6 -0
- data/config/initializers/tasker_orchestration.rb +17 -0
- data/config/initializers/time_formats.rb +4 -0
- data/config/routes.rb +34 -0
- data/config/tasker/subscriptions/example_integrations.yml +67 -0
- data/config/tasker/system_events.yml +305 -0
- data/db/functions/calculate_dependency_levels_v01.sql +45 -0
- data/db/functions/get_analytics_metrics_v01.sql +137 -0
- data/db/functions/get_slowest_steps_v01.sql +82 -0
- data/db/functions/get_slowest_tasks_v01.sql +96 -0
- data/db/functions/get_step_readiness_status_batch_v01.sql +140 -0
- data/db/functions/get_step_readiness_status_v01.sql +139 -0
- data/db/functions/get_system_health_counts_v01.sql +108 -0
- data/db/functions/get_task_execution_context_v01.sql +108 -0
- data/db/functions/get_task_execution_contexts_batch_v01.sql +104 -0
- data/db/init/schema.sql +2277 -0
- data/db/migrate/20250701165431_initial_tasker_schema.rb +116 -0
- data/db/views/tasker_step_dag_relationships_v01.sql +69 -0
- data/docs/APPLICATION_GENERATOR.md +384 -0
- data/docs/AUTH.md +1780 -0
- data/docs/CIRCUIT_BREAKER.md +224 -0
- data/docs/DEVELOPER_GUIDE.md +2665 -0
- data/docs/EVENT_SYSTEM.md +637 -0
- data/docs/EXECUTION_CONFIGURATION.md +341 -0
- data/docs/FLOW_CHART.md +149 -0
- data/docs/HEALTH.md +542 -0
- data/docs/METRICS.md +731 -0
- data/docs/OPTIMIZATION_PLAN.md +1479 -0
- data/docs/OVERVIEW.md +552 -0
- data/docs/QUICK_START.md +270 -0
- data/docs/REGISTRY_SYSTEMS.md +373 -0
- data/docs/REST_API.md +632 -0
- data/docs/ROADMAP.md +221 -0
- data/docs/SQL_FUNCTIONS.md +1408 -0
- data/docs/TASK_DIAGRAM.md +252 -0
- data/docs/TASK_EXECUTION_CONTROL_FLOW.md +237 -0
- data/docs/TELEMETRY.md +795 -0
- data/docs/TROUBLESHOOTING.md +756 -0
- data/docs/TaskHandlerGenerator.html +255 -0
- data/docs/Tasker/Analysis/RuntimeGraphAnalyzer.html +907 -0
- data/docs/Tasker/Analysis/TemplateGraphAnalyzer.html +1236 -0
- data/docs/Tasker/Analysis.html +117 -0
- data/docs/Tasker/AnalyticsController.html +450 -0
- data/docs/Tasker/AnalyticsService/BottleneckAnalytics.html +816 -0
- data/docs/Tasker/AnalyticsService/PerformanceAnalytics.html +586 -0
- data/docs/Tasker/AnalyticsService.html +2221 -0
- data/docs/Tasker/AnnotationType.html +137 -0
- data/docs/Tasker/AnnotationTypeSerializer.html +124 -0
- data/docs/Tasker/ApplicationController.html +147 -0
- data/docs/Tasker/ApplicationJob.html +128 -0
- data/docs/Tasker/ApplicationRecord.html +378 -0
- data/docs/Tasker/Authentication/AuthenticationError.html +124 -0
- data/docs/Tasker/Authentication/ConfigurationError.html +124 -0
- data/docs/Tasker/Authentication/Coordinator.html +242 -0
- data/docs/Tasker/Authentication/Interface.html +560 -0
- data/docs/Tasker/Authentication/InterfaceError.html +124 -0
- data/docs/Tasker/Authentication/NoneAuthenticator.html +338 -0
- data/docs/Tasker/Authentication.html +119 -0
- data/docs/Tasker/Authorization/AuthorizationError.html +139 -0
- data/docs/Tasker/Authorization/BaseCoordinator.html +927 -0
- data/docs/Tasker/Authorization/ConfigurationError.html +153 -0
- data/docs/Tasker/Authorization/ResourceConstants/ACTIONS.html +428 -0
- data/docs/Tasker/Authorization/ResourceConstants/RESOURCES.html +365 -0
- data/docs/Tasker/Authorization/ResourceConstants.html +146 -0
- data/docs/Tasker/Authorization/ResourceRegistry.html +882 -0
- data/docs/Tasker/Authorization/UnauthorizedError.html +153 -0
- data/docs/Tasker/Authorization.html +582 -0
- data/docs/Tasker/CacheCapabilities.html +167 -0
- data/docs/Tasker/CacheStrategy.html +1297 -0
- data/docs/Tasker/Concerns/Authenticatable.html +116 -0
- data/docs/Tasker/Concerns/Authorizable/AdminStatusChecker.html +256 -0
- data/docs/Tasker/Concerns/Authorizable.html +816 -0
- data/docs/Tasker/Concerns/ControllerAuthorizable.html +157 -0
- data/docs/Tasker/Concerns/EventPublisher.html +4023 -0
- data/docs/Tasker/Concerns/IdempotentStateTransitions.html +806 -0
- data/docs/Tasker/Concerns/LifecycleEventHelpers.html +129 -0
- data/docs/Tasker/Concerns/OrchestrationPublisher.html +129 -0
- data/docs/Tasker/Concerns/StateMachineBase/ClassMethods.html +1075 -0
- data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase/ClassMethods.html +191 -0
- data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase.html +126 -0
- data/docs/Tasker/Concerns/StateMachineBase.html +153 -0
- data/docs/Tasker/Concerns/StructuredLogging.html +1413 -0
- data/docs/Tasker/Concerns.html +117 -0
- data/docs/Tasker/Configuration/AuthConfiguration.html +1023 -0
- data/docs/Tasker/Configuration/ConfigurationProxy.html +581 -0
- data/docs/Tasker/Configuration/DatabaseConfiguration.html +475 -0
- data/docs/Tasker/Configuration/EngineConfiguration.html +1265 -0
- data/docs/Tasker/Configuration/HealthConfiguration.html +791 -0
- data/docs/Tasker/Configuration/TelemetryConfiguration.html +1308 -0
- data/docs/Tasker/Configuration/TelemetryConfigurationProxy.html +388 -0
- data/docs/Tasker/Configuration.html +1669 -0
- data/docs/Tasker/ConfigurationError.html +143 -0
- data/docs/Tasker/ConfiguredTask.html +514 -0
- data/docs/Tasker/Constants/EventDefinitions.html +590 -0
- data/docs/Tasker/Constants/LifecycleEvents.html +137 -0
- data/docs/Tasker/Constants/ObservabilityEvents/Step.html +152 -0
- data/docs/Tasker/Constants/ObservabilityEvents/Task.html +142 -0
- data/docs/Tasker/Constants/ObservabilityEvents.html +126 -0
- data/docs/Tasker/Constants/RegistryEvents.html +285 -0
- data/docs/Tasker/Constants/StepEvents.html +177 -0
- data/docs/Tasker/Constants/TaskEvents.html +167 -0
- data/docs/Tasker/Constants/TaskExecution/ExecutionStatus.html +207 -0
- data/docs/Tasker/Constants/TaskExecution/HealthStatus.html +191 -0
- data/docs/Tasker/Constants/TaskExecution/RecommendedAction.html +207 -0
- data/docs/Tasker/Constants/TaskExecution.html +126 -0
- data/docs/Tasker/Constants/TaskFinalization/ErrorMessages.html +132 -0
- data/docs/Tasker/Constants/TaskFinalization/PendingReasons.html +207 -0
- data/docs/Tasker/Constants/TaskFinalization/ReenqueueReasons.html +239 -0
- data/docs/Tasker/Constants/TaskFinalization.html +126 -0
- data/docs/Tasker/Constants/TaskStatuses.html +223 -0
- data/docs/Tasker/Constants/TestEvents.html +163 -0
- data/docs/Tasker/Constants/WorkflowEvents.html +222 -0
- data/docs/Tasker/Constants/WorkflowStepStatuses.html +223 -0
- data/docs/Tasker/Constants.html +561 -0
- data/docs/Tasker/DependentSystem.html +137 -0
- data/docs/Tasker/DependentSystemObjectMap.html +250 -0
- data/docs/Tasker/DetectorRegistry.html +598 -0
- data/docs/Tasker/Diagram/Edge.html +1191 -0
- data/docs/Tasker/Diagram/Flowchart.html +1539 -0
- data/docs/Tasker/Diagram/Node.html +1165 -0
- data/docs/Tasker/Diagram.html +117 -0
- data/docs/Tasker/Engine.html +215 -0
- data/docs/Tasker/Error.html +139 -0
- data/docs/Tasker/Events/Bus.html +1226 -0
- data/docs/Tasker/Events/Catalog/CatalogPrinter.html +258 -0
- data/docs/Tasker/Events/Catalog/CustomEventRegistrar.html +276 -0
- data/docs/Tasker/Events/Catalog/ExamplePayloadGenerator.html +294 -0
- data/docs/Tasker/Events/Catalog.html +1291 -0
- data/docs/Tasker/Events/CustomRegistry.html +943 -0
- data/docs/Tasker/Events/DefinitionLoader.html +575 -0
- data/docs/Tasker/Events/EventPayloadBuilder/ErrorInfoExtractor.html +286 -0
- data/docs/Tasker/Events/EventPayloadBuilder/StepPayloadBuilder.html +312 -0
- data/docs/Tasker/Events/EventPayloadBuilder.html +664 -0
- data/docs/Tasker/Events/Publisher.html +365 -0
- data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer/ErrorTypeClassifier.html +1128 -0
- data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer.html +270 -0
- data/docs/Tasker/Events/Subscribers/BaseSubscriber/MetricTagsExtractor.html +266 -0
- data/docs/Tasker/Events/Subscribers/BaseSubscriber.html +2556 -0
- data/docs/Tasker/Events/Subscribers/MetricsSubscriber.html +723 -0
- data/docs/Tasker/Events/Subscribers/TelemetrySubscriber.html +2251 -0
- data/docs/Tasker/Events/Subscribers.html +117 -0
- data/docs/Tasker/Events/SubscriptionLoader.html +493 -0
- data/docs/Tasker/Events.html +294 -0
- data/docs/Tasker/EventsGenerator.html +459 -0
- data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics/AnalyticsMetrics.html +135 -0
- data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics.html +412 -0
- data/docs/Tasker/Functions/FunctionBasedDependencyLevels.html +598 -0
- data/docs/Tasker/Functions/FunctionBasedSlowestSteps/SlowestStep.html +135 -0
- data/docs/Tasker/Functions/FunctionBasedSlowestSteps.html +453 -0
- data/docs/Tasker/Functions/FunctionBasedSlowestTasks/SlowestTask.html +135 -0
- data/docs/Tasker/Functions/FunctionBasedSlowestTasks.html +453 -0
- data/docs/Tasker/Functions/FunctionBasedStepReadinessStatus.html +1457 -0
- data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts/HealthMetrics.html +135 -0
- data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts.html +370 -0
- data/docs/Tasker/Functions/FunctionBasedTaskExecutionContext.html +1250 -0
- data/docs/Tasker/Functions/FunctionWrapper.html +479 -0
- data/docs/Tasker/Functions.html +117 -0
- data/docs/Tasker/Generators/AuthenticatorGenerator/UsageInstructionsFormatter.html +244 -0
- data/docs/Tasker/Generators/AuthenticatorGenerator.html +373 -0
- data/docs/Tasker/Generators/AuthorizationCoordinatorGenerator.html +430 -0
- data/docs/Tasker/Generators/SubscriberGenerator.html +377 -0
- data/docs/Tasker/Generators/TaskHandlerGenerator.html +263 -0
- data/docs/Tasker/Generators.html +117 -0
- data/docs/Tasker/GraphQLTypes/AnnotationType.html +132 -0
- data/docs/Tasker/GraphQLTypes/BaseArgument.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseConnection.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseEdge.html +130 -0
- data/docs/Tasker/GraphQLTypes/BaseEnum.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseField.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseInputObject.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseInterface.html +116 -0
- data/docs/Tasker/GraphQLTypes/BaseObject.html +128 -0
- data/docs/Tasker/GraphQLTypes/BaseScalar.html +124 -0
- data/docs/Tasker/GraphQLTypes/BaseUnion.html +124 -0
- data/docs/Tasker/GraphQLTypes/DependentSystemObjectMapType.html +132 -0
- data/docs/Tasker/GraphQLTypes/DependentSystemType.html +132 -0
- data/docs/Tasker/GraphQLTypes/MutationType.html +132 -0
- data/docs/Tasker/GraphQLTypes/NamedStepType.html +132 -0
- data/docs/Tasker/GraphQLTypes/NamedTaskType.html +132 -0
- data/docs/Tasker/GraphQLTypes/NamedTasksNamedStepType.html +132 -0
- data/docs/Tasker/GraphQLTypes/NodeType.html +118 -0
- data/docs/Tasker/GraphQLTypes/QueryType.html +139 -0
- data/docs/Tasker/GraphQLTypes/TaskAnnotationType.html +132 -0
- data/docs/Tasker/GraphQLTypes/TaskInterface.html +111 -0
- data/docs/Tasker/GraphQLTypes/TaskType.html +201 -0
- data/docs/Tasker/GraphQLTypes/WorkflowStepType.html +694 -0
- data/docs/Tasker/GraphQLTypes.html +130 -0
- data/docs/Tasker/GraphqlController.html +251 -0
- data/docs/Tasker/HandlerFactory.html +1518 -0
- data/docs/Tasker/HandlerSerializer.html +682 -0
- data/docs/Tasker/HandlersController.html +574 -0
- data/docs/Tasker/HashIdentityStrategy.html +278 -0
- data/docs/Tasker/Health/ReadinessChecker.html +712 -0
- data/docs/Tasker/Health/StatusChecker.html +653 -0
- data/docs/Tasker/Health.html +117 -0
- data/docs/Tasker/HealthController.html +523 -0
- data/docs/Tasker/IdentityStrategy.html +276 -0
- data/docs/Tasker/InvalidTaskHandlerConfig.html +135 -0
- data/docs/Tasker/LifecycleEvents/Events/Step.html +162 -0
- data/docs/Tasker/LifecycleEvents/Events/Task.html +162 -0
- data/docs/Tasker/LifecycleEvents/Events.html +204 -0
- data/docs/Tasker/LifecycleEvents/Publisher.html +132 -0
- data/docs/Tasker/LifecycleEvents.html +799 -0
- data/docs/Tasker/Logging/CorrelationIdGenerator.html +688 -0
- data/docs/Tasker/Logging.html +115 -0
- data/docs/Tasker/MetricsController.html +293 -0
- data/docs/Tasker/MetricsExportJob.html +414 -0
- data/docs/Tasker/Mutations/BaseMutation.html +128 -0
- data/docs/Tasker/Mutations/CancelStep.html +219 -0
- data/docs/Tasker/Mutations/CancelTask.html +221 -0
- data/docs/Tasker/Mutations/CreateTask.html +243 -0
- data/docs/Tasker/Mutations/UpdateStep.html +243 -0
- data/docs/Tasker/Mutations/UpdateTask.html +243 -0
- data/docs/Tasker/Mutations.html +117 -0
- data/docs/Tasker/NamedStep.html +216 -0
- data/docs/Tasker/NamedTask.html +910 -0
- data/docs/Tasker/NamedTasksNamedStep.html +435 -0
- data/docs/Tasker/Orchestration/BackoffCalculator.html +404 -0
- data/docs/Tasker/Orchestration/ConnectionBuilder/ConfigValidator.html +258 -0
- data/docs/Tasker/Orchestration/ConnectionBuilder.html +435 -0
- data/docs/Tasker/Orchestration/ConnectionPoolIntelligence.html +513 -0
- data/docs/Tasker/Orchestration/Coordinator.html +641 -0
- data/docs/Tasker/Orchestration/FutureStateAnalyzer.html +1045 -0
- data/docs/Tasker/Orchestration/Orchestrator.html +679 -0
- data/docs/Tasker/Orchestration/PluginIntegration.html +1127 -0
- data/docs/Tasker/Orchestration/ResponseProcessor.html +504 -0
- data/docs/Tasker/Orchestration/RetryHeaderParser.html +304 -0
- data/docs/Tasker/Orchestration/StepExecutor.html +995 -0
- data/docs/Tasker/Orchestration/StepSequenceFactory.html +644 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/BlockageChecker.html +264 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/ContextManager.html +254 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/DelayCalculator.html +556 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationDecisionMaker.html +348 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationProcessor.html +286 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/ReasonDeterminer.html +432 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/ReenqueueManager.html +296 -0
- data/docs/Tasker/Orchestration/TaskFinalizer/UnclearStateHandler.html +314 -0
- data/docs/Tasker/Orchestration/TaskFinalizer.html +1212 -0
- data/docs/Tasker/Orchestration/TaskInitializer.html +766 -0
- data/docs/Tasker/Orchestration/TaskReenqueuer.html +506 -0
- data/docs/Tasker/Orchestration/ViableStepDiscovery.html +442 -0
- data/docs/Tasker/Orchestration/WorkflowCoordinator.html +510 -0
- data/docs/Tasker/Orchestration.html +130 -0
- data/docs/Tasker/PageSort/PageSortParamsBuilder.html +296 -0
- data/docs/Tasker/PageSort.html +247 -0
- data/docs/Tasker/PermanentError.html +518 -0
- data/docs/Tasker/ProceduralError.html +147 -0
- data/docs/Tasker/Queries/AllAnnotationTypes.html +217 -0
- data/docs/Tasker/Queries/AllTasks.html +221 -0
- data/docs/Tasker/Queries/BaseQuery.html +128 -0
- data/docs/Tasker/Queries/Helpers.html +187 -0
- data/docs/Tasker/Queries/OneStep.html +225 -0
- data/docs/Tasker/Queries/OneTask.html +217 -0
- data/docs/Tasker/Queries/TasksByAnnotation.html +231 -0
- data/docs/Tasker/Queries/TasksByStatus.html +233 -0
- data/docs/Tasker/Queries.html +119 -0
- data/docs/Tasker/Railtie.html +124 -0
- data/docs/Tasker/Registry/BaseRegistry.html +1690 -0
- data/docs/Tasker/Registry/EventPublisher.html +667 -0
- data/docs/Tasker/Registry/InterfaceValidator.html +569 -0
- data/docs/Tasker/Registry/RegistrationError.html +132 -0
- data/docs/Tasker/Registry/RegistryError.html +139 -0
- data/docs/Tasker/Registry/StatisticsCollector.html +841 -0
- data/docs/Tasker/Registry/SubscriberRegistry.html +1504 -0
- data/docs/Tasker/Registry/ValidationError.html +132 -0
- data/docs/Tasker/Registry.html +119 -0
- data/docs/Tasker/RetryableError.html +515 -0
- data/docs/Tasker/StateMachine/Compatibility.html +282 -0
- data/docs/Tasker/StateMachine/InvalidStateTransition.html +135 -0
- data/docs/Tasker/StateMachine/StepStateMachine/StandardizedPayloadBuilder.html +260 -0
- data/docs/Tasker/StateMachine/StepStateMachine.html +2215 -0
- data/docs/Tasker/StateMachine/TaskStateMachine.html +734 -0
- data/docs/Tasker/StateMachine.html +602 -0
- data/docs/Tasker/StepDagRelationship.html +657 -0
- data/docs/Tasker/StepHandler/Api/Config.html +1091 -0
- data/docs/Tasker/StepHandler/Api.html +884 -0
- data/docs/Tasker/StepHandler/AutomaticEventPublishing.html +321 -0
- data/docs/Tasker/StepHandler/Base.html +970 -0
- data/docs/Tasker/StepHandler.html +119 -0
- data/docs/Tasker/StepReadinessStatus.html +836 -0
- data/docs/Tasker/Task.html +2575 -0
- data/docs/Tasker/TaskAnnotation.html +137 -0
- data/docs/Tasker/TaskAnnotationSerializer.html +124 -0
- data/docs/Tasker/TaskBuilder/StepNameValidator.html +264 -0
- data/docs/Tasker/TaskBuilder/StepTemplateDefiner.html +264 -0
- data/docs/Tasker/TaskBuilder.html +764 -0
- data/docs/Tasker/TaskDiagram/StepToStepEdgeBuilder.html +260 -0
- data/docs/Tasker/TaskDiagram/TaskToRootStepEdgeBuilder.html +290 -0
- data/docs/Tasker/TaskDiagram.html +548 -0
- data/docs/Tasker/TaskDiagramsController.html +240 -0
- data/docs/Tasker/TaskExecutionContext.html +469 -0
- data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/ClassBasedEventRegistrar.html +238 -0
- data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/YamlEventRegistrar.html +254 -0
- data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner.html +988 -0
- data/docs/Tasker/TaskHandler/ClassMethods.html +357 -0
- data/docs/Tasker/TaskHandler/InstanceMethods.html +1396 -0
- data/docs/Tasker/TaskHandler/StepGroup.html +1748 -0
- data/docs/Tasker/TaskHandler.html +271 -0
- data/docs/Tasker/TaskNamespace.html +312 -0
- data/docs/Tasker/TaskRunnerJob.html +406 -0
- data/docs/Tasker/TaskSerializer.html +474 -0
- data/docs/Tasker/TaskTransition.html +1517 -0
- data/docs/Tasker/TaskWorkflowSummary.html +988 -0
- data/docs/Tasker/TaskerRailsSchema/InvalidObjectTypeError.html +132 -0
- data/docs/Tasker/TaskerRailsSchema/TypeResolutionError.html +139 -0
- data/docs/Tasker/TaskerRailsSchema/UnknownInterfaceError.html +132 -0
- data/docs/Tasker/TaskerRailsSchema.html +384 -0
- data/docs/Tasker/TasksController.html +595 -0
- data/docs/Tasker/Telemetry/EventMapping.html +1307 -0
- data/docs/Tasker/Telemetry/EventRouter.html +2178 -0
- data/docs/Tasker/Telemetry/Events/ExportEvents.html +246 -0
- data/docs/Tasker/Telemetry/Events.html +115 -0
- data/docs/Tasker/Telemetry/ExportCoordinator/DistributedLockTimeoutError.html +135 -0
- data/docs/Tasker/Telemetry/ExportCoordinator.html +2137 -0
- data/docs/Tasker/Telemetry/IntelligentCacheManager.html +1083 -0
- data/docs/Tasker/Telemetry/LogBackend.html +1088 -0
- data/docs/Tasker/Telemetry/MetricTypes/Counter.html +1054 -0
- data/docs/Tasker/Telemetry/MetricTypes/Gauge.html +1270 -0
- data/docs/Tasker/Telemetry/MetricTypes/Histogram.html +1492 -0
- data/docs/Tasker/Telemetry/MetricTypes.html +153 -0
- data/docs/Tasker/Telemetry/MetricsBackend.html +2510 -0
- data/docs/Tasker/Telemetry/MetricsExportService.html +578 -0
- data/docs/Tasker/Telemetry/PluginRegistry.html +1774 -0
- data/docs/Tasker/Telemetry/Plugins/BaseExporter.html +1835 -0
- data/docs/Tasker/Telemetry/Plugins/CsvExporter.html +768 -0
- data/docs/Tasker/Telemetry/Plugins/JsonExporter.html +747 -0
- data/docs/Tasker/Telemetry/Plugins.html +117 -0
- data/docs/Tasker/Telemetry/PrometheusExporter.html +481 -0
- data/docs/Tasker/Telemetry/TraceBackend.html +891 -0
- data/docs/Tasker/Telemetry.html +130 -0
- data/docs/Tasker/Types/AuthConfig.html +886 -0
- data/docs/Tasker/Types/BackoffConfig.html +1063 -0
- data/docs/Tasker/Types/BaseConfig.html +227 -0
- data/docs/Tasker/Types/CacheConfig.html +1731 -0
- data/docs/Tasker/Types/DatabaseConfig.html +388 -0
- data/docs/Tasker/Types/DependencyGraph.html +526 -0
- data/docs/Tasker/Types/DependencyGraphConfig.html +753 -0
- data/docs/Tasker/Types/EngineConfig.html +1181 -0
- data/docs/Tasker/Types/ExecutionConfig.html +1963 -0
- data/docs/Tasker/Types/GraphEdge.html +517 -0
- data/docs/Tasker/Types/GraphMetadata.html +781 -0
- data/docs/Tasker/Types/GraphNode.html +694 -0
- data/docs/Tasker/Types/HealthConfig.html +784 -0
- data/docs/Tasker/Types/StepSequence.html +353 -0
- data/docs/Tasker/Types/StepTemplate.html +1193 -0
- data/docs/Tasker/Types/TaskRequest.html +1179 -0
- data/docs/Tasker/Types/TelemetryConfig.html +2746 -0
- data/docs/Tasker/Types.html +154 -0
- data/docs/Tasker/WorkflowStep/StepFinder.html +282 -0
- data/docs/Tasker/WorkflowStep.html +2724 -0
- data/docs/Tasker/WorkflowStepEdge.html +304 -0
- data/docs/Tasker/WorkflowStepSerializer.html +305 -0
- data/docs/Tasker/WorkflowStepTransition/TransitionDescriptionFormatter.html +282 -0
- data/docs/Tasker/WorkflowStepTransition.html +2201 -0
- data/docs/Tasker/WorkflowStepsController.html +462 -0
- data/docs/Tasker.html +452 -0
- data/docs/VISION.md +584 -0
- data/docs/WHY.md +21 -0
- data/docs/_index.html +2375 -0
- data/docs/class_list.html +54 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +503 -0
- data/docs/events/migration_plan_outcomes.md +80 -0
- data/docs/file.README.html +541 -0
- data/docs/file_list.html +59 -0
- data/docs/frames.html +22 -0
- data/docs/index.html +541 -0
- data/docs/js/app.js +344 -0
- data/docs/js/full_list.js +242 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +9182 -0
- data/docs/top-level-namespace.html +110 -0
- data/lib/generators/tasker/authenticator_generator.rb +301 -0
- data/lib/generators/tasker/authorization_coordinator_generator.rb +139 -0
- data/lib/generators/tasker/events_generator.rb +91 -0
- data/lib/generators/tasker/subscriber_generator.rb +107 -0
- data/lib/generators/tasker/task_handler_generator.rb +138 -0
- data/lib/generators/tasker/templates/api_token_authenticator.rb.erb +113 -0
- data/lib/generators/tasker/templates/api_token_authenticator_spec.rb.erb +144 -0
- data/lib/generators/tasker/templates/authorization_coordinator.rb.erb +95 -0
- data/lib/generators/tasker/templates/authorization_coordinator_spec.rb.erb +142 -0
- data/lib/generators/tasker/templates/custom_authenticator.rb.erb +108 -0
- data/lib/generators/tasker/templates/custom_authenticator_spec.rb.erb +162 -0
- data/lib/generators/tasker/templates/custom_events.yml.erb +62 -0
- data/lib/generators/tasker/templates/custom_subscriber.rb.erb +72 -0
- data/lib/generators/tasker/templates/devise_authenticator.rb.erb +101 -0
- data/lib/generators/tasker/templates/devise_authenticator_spec.rb.erb +126 -0
- data/lib/generators/tasker/templates/initialize.rb.erb +202 -0
- data/lib/generators/tasker/templates/jwt_authenticator.rb.erb +144 -0
- data/lib/generators/tasker/templates/jwt_authenticator_spec.rb.erb +298 -0
- data/lib/generators/tasker/templates/metrics_subscriber.rb.erb +258 -0
- data/lib/generators/tasker/templates/metrics_subscriber_spec.rb.erb +308 -0
- data/lib/generators/tasker/templates/omniauth_authenticator.rb.erb +135 -0
- data/lib/generators/tasker/templates/omniauth_authenticator_spec.rb.erb +196 -0
- data/lib/generators/tasker/templates/opentelemetry_initializer.rb +52 -0
- data/lib/generators/tasker/templates/subscriber.rb.erb +64 -0
- data/lib/generators/tasker/templates/subscriber_spec.rb.erb +80 -0
- data/lib/generators/tasker/templates/task_config.yaml.erb +117 -0
- data/lib/generators/tasker/templates/task_handler.rb.erb +59 -0
- data/lib/generators/tasker/templates/task_handler_spec.rb.erb +159 -0
- data/lib/tasker/analysis/runtime_graph_analyzer.rb +1168 -0
- data/lib/tasker/analysis/template_graph_analyzer.rb +328 -0
- data/lib/tasker/authentication/coordinator.rb +78 -0
- data/lib/tasker/authentication/errors.rb +9 -0
- data/lib/tasker/authentication/interface.rb +36 -0
- data/lib/tasker/authentication/none_authenticator.rb +26 -0
- data/lib/tasker/authorization/base_coordinator.rb +112 -0
- data/lib/tasker/authorization/errors.rb +26 -0
- data/lib/tasker/authorization/resource_constants.rb +74 -0
- data/lib/tasker/authorization/resource_registry.rb +143 -0
- data/lib/tasker/authorization.rb +75 -0
- data/lib/tasker/cache_capabilities.rb +131 -0
- data/lib/tasker/cache_strategy.rb +469 -0
- data/lib/tasker/concerns/authenticatable.rb +41 -0
- data/lib/tasker/concerns/authorizable.rb +204 -0
- data/lib/tasker/concerns/controller_authorizable.rb +124 -0
- data/lib/tasker/concerns/event_publisher.rb +716 -0
- data/lib/tasker/concerns/idempotent_state_transitions.rb +128 -0
- data/lib/tasker/concerns/state_machine_base.rb +218 -0
- data/lib/tasker/concerns/structured_logging.rb +387 -0
- data/lib/tasker/configuration.rb +325 -0
- data/lib/tasker/constants/event_definitions.rb +147 -0
- data/lib/tasker/constants/registry_events.rb +54 -0
- data/lib/tasker/constants.rb +417 -0
- data/lib/tasker/engine.rb +90 -0
- data/lib/tasker/errors.rb +90 -0
- data/lib/tasker/events/catalog.rb +432 -0
- data/lib/tasker/events/custom_registry.rb +175 -0
- data/lib/tasker/events/definition_loader.rb +199 -0
- data/lib/tasker/events/event_payload_builder.rb +461 -0
- data/lib/tasker/events/publisher.rb +149 -0
- data/lib/tasker/events/subscribers/base_subscriber.rb +601 -0
- data/lib/tasker/events/subscribers/metrics_subscriber.rb +120 -0
- data/lib/tasker/events/subscribers/telemetry_subscriber.rb +462 -0
- data/lib/tasker/events/subscription_loader.rb +161 -0
- data/lib/tasker/events.rb +37 -0
- data/lib/tasker/functions/function_based_analytics_metrics.rb +103 -0
- data/lib/tasker/functions/function_based_dependency_levels.rb +54 -0
- data/lib/tasker/functions/function_based_slowest_steps.rb +84 -0
- data/lib/tasker/functions/function_based_slowest_tasks.rb +84 -0
- data/lib/tasker/functions/function_based_step_readiness_status.rb +183 -0
- data/lib/tasker/functions/function_based_system_health_counts.rb +94 -0
- data/lib/tasker/functions/function_based_task_execution_context.rb +148 -0
- data/lib/tasker/functions/function_wrapper.rb +42 -0
- data/lib/tasker/functions.rb +12 -0
- data/lib/tasker/handler_factory.rb +322 -0
- data/lib/tasker/health/readiness_checker.rb +186 -0
- data/lib/tasker/health/status_checker.rb +203 -0
- data/lib/tasker/identity_strategy.rb +38 -0
- data/lib/tasker/logging/correlation_id_generator.rb +120 -0
- data/lib/tasker/orchestration/backoff_calculator.rb +184 -0
- data/lib/tasker/orchestration/connection_builder.rb +122 -0
- data/lib/tasker/orchestration/connection_pool_intelligence.rb +177 -0
- data/lib/tasker/orchestration/coordinator.rb +119 -0
- data/lib/tasker/orchestration/future_state_analyzer.rb +137 -0
- data/lib/tasker/orchestration/plugin_integration.rb +124 -0
- data/lib/tasker/orchestration/response_processor.rb +168 -0
- data/lib/tasker/orchestration/retry_header_parser.rb +78 -0
- data/lib/tasker/orchestration/step_executor.rb +941 -0
- data/lib/tasker/orchestration/step_sequence_factory.rb +67 -0
- data/lib/tasker/orchestration/task_finalizer.rb +564 -0
- data/lib/tasker/orchestration/task_initializer.rb +140 -0
- data/lib/tasker/orchestration/task_reenqueuer.rb +71 -0
- data/lib/tasker/orchestration/viable_step_discovery.rb +65 -0
- data/lib/tasker/orchestration/workflow_coordinator.rb +294 -0
- data/lib/tasker/orchestration.rb +45 -0
- data/lib/tasker/railtie.rb +9 -0
- data/lib/tasker/registry/base_registry.rb +177 -0
- data/lib/tasker/registry/event_publisher.rb +91 -0
- data/lib/tasker/registry/interface_validator.rb +140 -0
- data/lib/tasker/registry/statistics_collector.rb +381 -0
- data/lib/tasker/registry/subscriber_registry.rb +285 -0
- data/lib/tasker/registry.rb +22 -0
- data/lib/tasker/state_machine/step_state_machine.rb +508 -0
- data/lib/tasker/state_machine/task_state_machine.rb +192 -0
- data/lib/tasker/state_machine.rb +83 -0
- data/lib/tasker/step_handler/api.rb +410 -0
- data/lib/tasker/step_handler/base.rb +206 -0
- data/lib/tasker/task_builder.rb +432 -0
- data/lib/tasker/task_handler/class_methods.rb +324 -0
- data/lib/tasker/task_handler/instance_methods.rb +293 -0
- data/lib/tasker/task_handler/step_group.rb +182 -0
- data/lib/tasker/task_handler.rb +43 -0
- data/lib/tasker/telemetry/event_mapping.rb +126 -0
- data/lib/tasker/telemetry/event_router.rb +318 -0
- data/lib/tasker/telemetry/events/export_events.rb +38 -0
- data/lib/tasker/telemetry/export_coordinator.rb +497 -0
- data/lib/tasker/telemetry/intelligent_cache_manager.rb +508 -0
- data/lib/tasker/telemetry/log_backend.rb +224 -0
- data/lib/tasker/telemetry/metric_types.rb +368 -0
- data/lib/tasker/telemetry/metrics_backend.rb +1227 -0
- data/lib/tasker/telemetry/metrics_export_service.rb +392 -0
- data/lib/tasker/telemetry/plugin_registry.rb +333 -0
- data/lib/tasker/telemetry/plugins/base_exporter.rb +246 -0
- data/lib/tasker/telemetry/plugins/csv_exporter.rb +198 -0
- data/lib/tasker/telemetry/plugins/json_exporter.rb +141 -0
- data/lib/tasker/telemetry/prometheus_exporter.rb +249 -0
- data/lib/tasker/telemetry/trace_backend.rb +186 -0
- data/lib/tasker/telemetry.rb +59 -0
- data/lib/tasker/types/auth_config.rb +81 -0
- data/lib/tasker/types/backoff_config.rb +142 -0
- data/lib/tasker/types/cache_config.rb +257 -0
- data/lib/tasker/types/database_config.rb +39 -0
- data/lib/tasker/types/dependency_graph.rb +225 -0
- data/lib/tasker/types/dependency_graph_config.rb +149 -0
- data/lib/tasker/types/engine_config.rb +131 -0
- data/lib/tasker/types/execution_config.rb +289 -0
- data/lib/tasker/types/health_config.rb +84 -0
- data/lib/tasker/types/step_sequence.rb +24 -0
- data/lib/tasker/types/step_template.rb +63 -0
- data/lib/tasker/types/task_request.rb +60 -0
- data/lib/tasker/types/telemetry_config.rb +273 -0
- data/lib/tasker/types.rb +64 -0
- data/lib/tasker/version.rb +7 -0
- data/lib/tasker.rb +82 -0
- data/lib/tasks/tasker_tasks.rake +302 -0
- metadata +958 -0
@@ -0,0 +1,1408 @@
|
|
1
|
+
# Tasker SQL Functions Documentation
|
2
|
+
|
3
|
+
This document provides detailed technical documentation for the core SQL functions that power Tasker's workflow execution engine. These functions are critical performance components that enable efficient step readiness calculations and task execution context analysis.
|
4
|
+
|
5
|
+
## Executive Summary
|
6
|
+
|
7
|
+
**Mission**: Eliminate database query timeouts and enable the Tasker workflow orchestration system to handle enterprise-scale workloads with millions of historical tasks while maintaining sub-second operational performance.
|
8
|
+
|
9
|
+
**Problem Solved**: Database views processing ALL tasks and steps, including completed ones, leading to performance degradation that scales with total historical data rather than active workload.
|
10
|
+
|
11
|
+
**Core Insight**: Active operations only need to consider incomplete tasks and unprocessed steps. By filtering out completed items early, query performance scales with active workload rather than total historical data.
|
12
|
+
|
13
|
+
## Performance Achievements
|
14
|
+
|
15
|
+
### Final Performance Results
|
16
|
+
| Metric | Before | After SQL Functions | Improvement |
|
17
|
+
|--------|--------|-------------------|-------------|
|
18
|
+
| 50 tasks | 2-5 seconds | <50ms | **50-100x faster** |
|
19
|
+
| 500 tasks | 30+ seconds (timeout) | <100ms | **300x+ faster** |
|
20
|
+
| 5,000 tasks | Unusable | <500ms | **Production ready** |
|
21
|
+
| 50,000 tasks | Impossible | <2 seconds | **Enterprise scale** |
|
22
|
+
| 1M+ tasks | N/A | <5 seconds | **Future-proof** |
|
23
|
+
|
24
|
+
### Detailed Function Performance
|
25
|
+
| Operation | Before (Views) | After (Functions) | Improvement |
|
26
|
+
|-----------|---------------|-------------------|-------------|
|
27
|
+
| Individual Step Readiness | 0.035s | 0.008s | **4.4x faster** |
|
28
|
+
| Batch Step Readiness | 0.022s | 0.005s | **4.4x faster** |
|
29
|
+
| Task Context Individual | 0.022s | 0.005s | **4.4x faster** |
|
30
|
+
| Task Context Batch | 0.008s | 0.003s | **2.7x faster** |
|
31
|
+
| Functions vs Views | Views: 0.011s | Functions: 0.008s | **38% faster** |
|
32
|
+
|
33
|
+
## Overview
|
34
|
+
|
35
|
+
The Tasker system uses eight key SQL functions to optimize workflow execution:
|
36
|
+
|
37
|
+
1. **`get_step_readiness_status`** - Analyzes step readiness for a single task
|
38
|
+
2. **`get_step_readiness_status_batch`** - Batch analysis for multiple tasks
|
39
|
+
3. **`get_task_execution_context`** - Provides execution context for a single task
|
40
|
+
4. **`get_task_execution_contexts_batch`** - Batch execution context for multiple tasks
|
41
|
+
5. **`calculate_dependency_levels`** - Calculates dependency levels for workflow steps
|
42
|
+
6. **`get_analytics_metrics_v01`** - High-performance analytics aggregation for system metrics
|
43
|
+
7. **`get_slowest_tasks_v01`** - Task performance analysis with filtering for bottleneck identification
|
44
|
+
8. **`get_slowest_steps_v01`** - Step-level performance analysis for detailed bottleneck investigation
|
45
|
+
|
46
|
+
These functions replace expensive view-based queries with optimized stored procedures, providing O(1) performance for critical workflow decisions.
|
47
|
+
|
48
|
+
## Function 1: `get_step_readiness_status`
|
49
|
+
|
50
|
+
### Purpose
|
51
|
+
Determines which workflow steps are ready for execution within a single task, handling dependency analysis, retry logic, and backoff timing.
|
52
|
+
|
53
|
+
### Signature
|
54
|
+
```sql
|
55
|
+
get_step_readiness_status(input_task_id BIGINT, step_ids BIGINT[] DEFAULT NULL)
|
56
|
+
```
|
57
|
+
|
58
|
+
### Input Parameters
|
59
|
+
- `input_task_id`: The task ID to analyze
|
60
|
+
- `step_ids`: Optional array to filter specific steps (NULL = all steps)
|
61
|
+
|
62
|
+
### Return Columns
|
63
|
+
| Column | Type | Description |
|
64
|
+
|--------|------|-------------|
|
65
|
+
| `workflow_step_id` | BIGINT | Unique step identifier |
|
66
|
+
| `task_id` | BIGINT | Parent task identifier |
|
67
|
+
| `named_step_id` | INTEGER | Step template reference |
|
68
|
+
| `name` | TEXT | Human-readable step name |
|
69
|
+
| `current_state` | TEXT | Current step state (pending, in_progress, complete, error, etc.) |
|
70
|
+
| `dependencies_satisfied` | BOOLEAN | Whether all parent steps are complete |
|
71
|
+
| `retry_eligible` | BOOLEAN | Whether step can be retried if failed |
|
72
|
+
| `ready_for_execution` | BOOLEAN | **CRITICAL**: Whether step can execute right now |
|
73
|
+
| `last_failure_at` | TIMESTAMP | When step last failed (NULL if never failed) |
|
74
|
+
| `next_retry_at` | TIMESTAMP | When step can next be retried (NULL if ready now) |
|
75
|
+
| `total_parents` | INTEGER | Number of dependency parents |
|
76
|
+
| `completed_parents` | INTEGER | Number of completed dependencies |
|
77
|
+
| `attempts` | INTEGER | Number of execution attempts |
|
78
|
+
| `retry_limit` | INTEGER | Maximum retry attempts allowed |
|
79
|
+
| `backoff_request_seconds` | INTEGER | Explicit backoff period (overrides exponential) |
|
80
|
+
| `last_attempted_at` | TIMESTAMP | When step was last attempted |
|
81
|
+
|
82
|
+
### Core Logic
|
83
|
+
|
84
|
+
#### 1. Current State Determination
|
85
|
+
```sql
|
86
|
+
COALESCE(current_state.to_state, 'pending')::TEXT as current_state
|
87
|
+
```
|
88
|
+
- Uses `most_recent = true` flag for O(1) state lookup
|
89
|
+
- Defaults to 'pending' for new steps
|
90
|
+
- Joins with `tasker_workflow_step_transitions` table
|
91
|
+
|
92
|
+
#### 2. Dependency Analysis
|
93
|
+
```sql
|
94
|
+
CASE
|
95
|
+
WHEN dep_edges.to_step_id IS NULL THEN true -- Root steps (no parents)
|
96
|
+
WHEN COUNT(dep_edges.from_step_id) = 0 THEN true -- Steps with zero dependencies
|
97
|
+
WHEN COUNT(CASE WHEN parent_states.to_state IN ('complete', 'resolved_manually') THEN 1 END) = COUNT(dep_edges.from_step_id) THEN true
|
98
|
+
ELSE false
|
99
|
+
END as dependencies_satisfied
|
100
|
+
```
|
101
|
+
- **Root Steps**: No incoming edges = automatically satisfied
|
102
|
+
- **Dependency Counting**: Compares completed parents vs total parents
|
103
|
+
- **Valid Completion States**: `'complete'` and `'resolved_manually'`
|
104
|
+
|
105
|
+
#### 3. Retry Eligibility
|
106
|
+
```sql
|
107
|
+
CASE
|
108
|
+
WHEN ws.attempts >= COALESCE(ws.retry_limit, 3) THEN false
|
109
|
+
WHEN ws.attempts > 0 AND COALESCE(ws.retryable, true) = false THEN false
|
110
|
+
WHEN last_failure.created_at IS NULL THEN true
|
111
|
+
WHEN ws.backoff_request_seconds IS NOT NULL AND ws.last_attempted_at IS NOT NULL THEN
|
112
|
+
ws.last_attempted_at + (ws.backoff_request_seconds * interval '1 second') <= NOW()
|
113
|
+
WHEN last_failure.created_at IS NOT NULL THEN
|
114
|
+
last_failure.created_at + (LEAST(power(2, COALESCE(ws.attempts, 1)) * interval '1 second', interval '30 seconds')) <= NOW()
|
115
|
+
ELSE true
|
116
|
+
END as retry_eligible
|
117
|
+
```
|
118
|
+
- **Retry Exhaustion**: `attempts >= retry_limit` (default 3)
|
119
|
+
- **Explicit Non-Retryable**: `retryable = false`
|
120
|
+
- **Explicit Backoff**: Uses step-defined backoff period
|
121
|
+
- **Exponential Backoff**: `2^attempts` seconds, capped at 30 seconds
|
122
|
+
- **Never Failed**: Always eligible
|
123
|
+
|
124
|
+
#### 4. Final Readiness Calculation
|
125
|
+
The most critical logic - determines if a step can execute **right now**:
|
126
|
+
|
127
|
+
```sql
|
128
|
+
CASE
|
129
|
+
WHEN COALESCE(current_state.to_state, 'pending') IN ('pending', 'error')
|
130
|
+
AND (ws.processed = false OR ws.processed IS NULL) -- CRITICAL: Only unprocessed steps
|
131
|
+
AND (dependencies_satisfied = true)
|
132
|
+
AND (ws.attempts < COALESCE(ws.retry_limit, 3))
|
133
|
+
AND (COALESCE(ws.retryable, true) = true)
|
134
|
+
AND (ws.in_process = false OR ws.in_process IS NULL)
|
135
|
+
AND (backoff_timing_satisfied = true)
|
136
|
+
THEN true
|
137
|
+
ELSE false
|
138
|
+
END as ready_for_execution
|
139
|
+
```
|
140
|
+
|
141
|
+
**ALL conditions must be true:**
|
142
|
+
1. **State Check**: Must be `'pending'` or `'error'`
|
143
|
+
2. **Processing Flag**: Must be unprocessed (`processed = false`)
|
144
|
+
3. **Dependencies**: All parent steps complete
|
145
|
+
4. **Retry Budget**: Haven't exhausted retry attempts
|
146
|
+
5. **Retryability**: Step allows retries
|
147
|
+
6. **Concurrency**: Not currently being processed (`in_process = false`)
|
148
|
+
7. **Timing**: Backoff period has elapsed
|
149
|
+
|
150
|
+
### Rails Integration
|
151
|
+
|
152
|
+
#### Primary Wrapper: `Tasker::Functions::FunctionBasedStepReadinessStatus`
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
# Get readiness for all steps in a task
|
156
|
+
statuses = Tasker::Functions::FunctionBasedStepReadinessStatus.for_task(task_id)
|
157
|
+
|
158
|
+
# Get readiness for specific steps
|
159
|
+
statuses = Tasker::Functions::FunctionBasedStepReadinessStatus.for_task(task_id, [step_id_1, step_id_2])
|
160
|
+
|
161
|
+
# Get only ready steps
|
162
|
+
ready_steps = Tasker::Functions::FunctionBasedStepReadinessStatus.ready_for_task(task_id)
|
163
|
+
```
|
164
|
+
|
165
|
+
#### Legacy Compatibility: `Tasker::StepReadinessStatus`
|
166
|
+
Delegates to the function-based implementation:
|
167
|
+
```ruby
|
168
|
+
# These all use the SQL function under the hood
|
169
|
+
Tasker::StepReadinessStatus.for_task(task_id)
|
170
|
+
Tasker::StepReadinessStatus.ready_for_task(task_id)
|
171
|
+
```
|
172
|
+
|
173
|
+
### Workflow Lifecycle Integration
|
174
|
+
|
175
|
+
#### 1. **Step Discovery Phase**
|
176
|
+
```ruby
|
177
|
+
# lib/tasker/orchestration/viable_step_discovery.rb
|
178
|
+
def find_viable_steps(task, sequence)
|
179
|
+
viable_steps = Tasker::WorkflowStep.get_viable_steps(task, sequence)
|
180
|
+
# Uses StepReadinessStatus.for_task internally
|
181
|
+
end
|
182
|
+
```
|
183
|
+
|
184
|
+
#### 2. **Workflow Execution**
|
185
|
+
```ruby
|
186
|
+
# app/models/tasker/workflow_step.rb
|
187
|
+
def self.get_viable_steps(task, sequence)
|
188
|
+
ready_statuses = StepReadinessStatus.for_task(task, step_ids)
|
189
|
+
ready_step_ids = ready_statuses.select(&:ready_for_execution).map(&:workflow_step_id)
|
190
|
+
WorkflowStep.where(workflow_step_id: ready_step_ids)
|
191
|
+
end
|
192
|
+
```
|
193
|
+
|
194
|
+
#### 3. **Individual Step Status Checking**
|
195
|
+
```ruby
|
196
|
+
# app/models/tasker/workflow_step.rb
|
197
|
+
def ready?
|
198
|
+
step_readiness_status&.ready_for_execution || false
|
199
|
+
end
|
200
|
+
|
201
|
+
def dependencies_satisfied?
|
202
|
+
step_readiness_status&.dependencies_satisfied || false
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
206
|
+
---
|
207
|
+
|
208
|
+
## Function 2: `get_step_readiness_status_batch`
|
209
|
+
|
210
|
+
### Purpose
|
211
|
+
Optimized batch version that analyzes step readiness for multiple tasks in a single query, avoiding N+1 performance issues.
|
212
|
+
|
213
|
+
### Signature
|
214
|
+
```sql
|
215
|
+
get_step_readiness_status_batch(input_task_ids BIGINT[])
|
216
|
+
```
|
217
|
+
|
218
|
+
### Input Parameters
|
219
|
+
- `input_task_ids`: Array of task IDs to analyze
|
220
|
+
|
221
|
+
### Return Columns
|
222
|
+
Identical to single-task version, with results grouped by `task_id`.
|
223
|
+
|
224
|
+
### Key Optimizations
|
225
|
+
1. **Single Query**: Processes multiple tasks without N+1 queries
|
226
|
+
2. **Task Filtering**: `WHERE ws.task_id = ANY(input_task_ids)`
|
227
|
+
3. **Consistent Ordering**: `ORDER BY ws.task_id, ws.workflow_step_id`
|
228
|
+
|
229
|
+
### Logic Differences
|
230
|
+
The core readiness logic is identical to the single-task version, but:
|
231
|
+
- Results are grouped by task_id for efficient batch processing
|
232
|
+
- No step_id filtering (returns all steps for each task)
|
233
|
+
- Optimized for bulk operations
|
234
|
+
|
235
|
+
### Rails Integration
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
# Process multiple tasks efficiently
|
239
|
+
task_ids = [1, 2, 3, 4, 5]
|
240
|
+
all_statuses = Tasker::Functions::FunctionBasedStepReadinessStatus.for_tasks(task_ids)
|
241
|
+
|
242
|
+
# Group by task
|
243
|
+
statuses_by_task = all_statuses.group_by(&:task_id)
|
244
|
+
```
|
245
|
+
|
246
|
+
---
|
247
|
+
|
248
|
+
## Function 3: `get_task_execution_context`
|
249
|
+
|
250
|
+
### Purpose
|
251
|
+
Provides high-level execution analysis for a single task, aggregating step readiness data into actionable workflow decisions.
|
252
|
+
|
253
|
+
### Signature
|
254
|
+
```sql
|
255
|
+
get_task_execution_context(input_task_id BIGINT)
|
256
|
+
```
|
257
|
+
|
258
|
+
### Return Columns
|
259
|
+
| Column | Type | Description |
|
260
|
+
|--------|------|-------------|
|
261
|
+
| `task_id` | BIGINT | Task identifier |
|
262
|
+
| `named_task_id` | INTEGER | Task template reference |
|
263
|
+
| `status` | TEXT | Current task status |
|
264
|
+
| `total_steps` | BIGINT | Total number of workflow steps |
|
265
|
+
| `pending_steps` | BIGINT | Steps in pending state |
|
266
|
+
| `in_progress_steps` | BIGINT | Steps currently executing |
|
267
|
+
| `completed_steps` | BIGINT | Successfully completed steps |
|
268
|
+
| `failed_steps` | BIGINT | Steps in error state |
|
269
|
+
| `ready_steps` | BIGINT | **CRITICAL**: Steps ready for immediate execution |
|
270
|
+
| `execution_status` | TEXT | High-level workflow state |
|
271
|
+
| `recommended_action` | TEXT | What should happen next |
|
272
|
+
| `completion_percentage` | DECIMAL | Progress percentage (0.0-100.0) |
|
273
|
+
| `health_status` | TEXT | Overall workflow health |
|
274
|
+
|
275
|
+
### Core Logic Architecture
|
276
|
+
|
277
|
+
#### Step 1: Data Collection
|
278
|
+
```sql
|
279
|
+
WITH step_data AS (
|
280
|
+
SELECT * FROM get_step_readiness_status(input_task_id, NULL)
|
281
|
+
),
|
282
|
+
task_info AS (
|
283
|
+
SELECT task_id, named_task_id, COALESCE(task_state.to_state, 'pending') as current_status
|
284
|
+
FROM tasker_tasks t LEFT JOIN tasker_task_transitions...
|
285
|
+
)
|
286
|
+
```
|
287
|
+
- **Reuses Step Readiness**: Builds on `get_step_readiness_status` output
|
288
|
+
- **Task State**: Gets current task status from transitions
|
289
|
+
|
290
|
+
#### Step 2: Statistical Aggregation
|
291
|
+
```sql
|
292
|
+
aggregated_stats AS (
|
293
|
+
SELECT
|
294
|
+
COUNT(*) as total_steps,
|
295
|
+
COUNT(CASE WHEN sd.current_state = 'pending' THEN 1 END) as pending_steps,
|
296
|
+
COUNT(CASE WHEN sd.current_state = 'in_progress' THEN 1 END) as in_progress_steps,
|
297
|
+
COUNT(CASE WHEN sd.current_state IN ('complete', 'resolved_manually') THEN 1 END) as completed_steps,
|
298
|
+
COUNT(CASE WHEN sd.current_state = 'error' THEN 1 END) as failed_steps,
|
299
|
+
COUNT(CASE WHEN sd.ready_for_execution = true THEN 1 END) as ready_steps,
|
300
|
+
-- CRITICAL: Only count permanently blocked failures
|
301
|
+
COUNT(CASE WHEN sd.current_state = 'error' AND (sd.attempts >= sd.retry_limit) THEN 1 END) as permanently_blocked_steps
|
302
|
+
FROM step_data sd
|
303
|
+
)
|
304
|
+
```
|
305
|
+
|
306
|
+
#### Step 3: Execution Status Logic
|
307
|
+
```sql
|
308
|
+
CASE
|
309
|
+
WHEN COALESCE(ast.ready_steps, 0) > 0 THEN 'has_ready_steps'
|
310
|
+
WHEN COALESCE(ast.in_progress_steps, 0) > 0 THEN 'processing'
|
311
|
+
WHEN COALESCE(ast.permanently_blocked_steps, 0) > 0 AND COALESCE(ast.ready_steps, 0) = 0 THEN 'blocked_by_failures'
|
312
|
+
WHEN COALESCE(ast.completed_steps, 0) = COALESCE(ast.total_steps, 0) AND COALESCE(ast.total_steps, 0) > 0 THEN 'all_complete'
|
313
|
+
ELSE 'waiting_for_dependencies'
|
314
|
+
END as execution_status
|
315
|
+
```
|
316
|
+
|
317
|
+
**Status Priority (highest to lowest):**
|
318
|
+
1. **`has_ready_steps`**: Can make immediate progress
|
319
|
+
2. **`processing`**: Work is currently happening
|
320
|
+
3. **`blocked_by_failures`**: Failed steps with no retry options
|
321
|
+
4. **`all_complete`**: Workflow finished successfully
|
322
|
+
5. **`waiting_for_dependencies`**: Default state
|
323
|
+
|
324
|
+
#### Step 4: Recommended Actions
|
325
|
+
```sql
|
326
|
+
CASE
|
327
|
+
WHEN COALESCE(ast.ready_steps, 0) > 0 THEN 'execute_ready_steps'
|
328
|
+
WHEN COALESCE(ast.in_progress_steps, 0) > 0 THEN 'wait_for_completion'
|
329
|
+
WHEN COALESCE(ast.permanently_blocked_steps, 0) > 0 AND COALESCE(ast.ready_steps, 0) = 0 THEN 'handle_failures'
|
330
|
+
WHEN COALESCE(ast.completed_steps, 0) = COALESCE(ast.total_steps, 0) AND COALESCE(ast.total_steps, 0) > 0 THEN 'finalize_task'
|
331
|
+
ELSE 'wait_for_dependencies'
|
332
|
+
END as recommended_action
|
333
|
+
```
|
334
|
+
|
335
|
+
#### Step 5: Health Status
|
336
|
+
```sql
|
337
|
+
CASE
|
338
|
+
WHEN COALESCE(ast.failed_steps, 0) = 0 THEN 'healthy'
|
339
|
+
WHEN COALESCE(ast.failed_steps, 0) > 0 AND COALESCE(ast.ready_steps, 0) > 0 THEN 'recovering'
|
340
|
+
WHEN COALESCE(ast.permanently_blocked_steps, 0) > 0 AND COALESCE(ast.ready_steps, 0) = 0 THEN 'blocked'
|
341
|
+
WHEN COALESCE(ast.failed_steps, 0) > 0 AND COALESCE(ast.permanently_blocked_steps, 0) = 0 AND COALESCE(ast.ready_steps, 0) = 0 THEN 'recovering'
|
342
|
+
ELSE 'unknown'
|
343
|
+
END as health_status
|
344
|
+
```
|
345
|
+
|
346
|
+
### Critical Bug Fix: Retry-Eligible vs Permanently Blocked
|
347
|
+
|
348
|
+
**The Problem**: Original logic incorrectly treated ALL failed steps as permanently blocked:
|
349
|
+
```sql
|
350
|
+
-- OLD BUG: Any failure = blocked
|
351
|
+
WHEN COALESCE(ast.failed_steps, 0) > 0 AND COALESCE(ast.ready_steps, 0) = 0 THEN 'blocked_by_failures'
|
352
|
+
```
|
353
|
+
|
354
|
+
**The Fix**: Only count failures that have exhausted retries:
|
355
|
+
```sql
|
356
|
+
-- NEW FIX: Only truly blocked failures
|
357
|
+
COUNT(CASE WHEN sd.current_state = 'error' AND (sd.attempts >= sd.retry_limit) THEN 1 END) as permanently_blocked_steps
|
358
|
+
```
|
359
|
+
|
360
|
+
This ensures steps in exponential backoff aren't incorrectly marked as blocked.
|
361
|
+
|
362
|
+
### Rails Integration
|
363
|
+
|
364
|
+
#### Primary Wrapper: `Tasker::Functions::FunctionBasedTaskExecutionContext`
|
365
|
+
|
366
|
+
```ruby
|
367
|
+
# Get execution context for a task
|
368
|
+
context = Tasker::Functions::FunctionBasedTaskExecutionContext.find(task_id)
|
369
|
+
|
370
|
+
# Check workflow state
|
371
|
+
if context.has_work_to_do?
|
372
|
+
# Task has ready steps or is processing
|
373
|
+
end
|
374
|
+
|
375
|
+
if context.is_blocked?
|
376
|
+
# Task has permanently failed steps
|
377
|
+
end
|
378
|
+
```
|
379
|
+
|
380
|
+
#### Usage in TaskFinalizer
|
381
|
+
```ruby
|
382
|
+
# lib/tasker/orchestration/task_finalizer.rb
|
383
|
+
def finalize_task(task_id, synchronous: false)
|
384
|
+
task = Tasker::Task.find(task_id)
|
385
|
+
context = ContextManager.get_task_execution_context(task_id)
|
386
|
+
|
387
|
+
case context.execution_status
|
388
|
+
when Constants::TaskExecution::ExecutionStatus::ALL_COMPLETE
|
389
|
+
complete_task(task, context)
|
390
|
+
when Constants::TaskExecution::ExecutionStatus::BLOCKED_BY_FAILURES
|
391
|
+
error_task(task, context)
|
392
|
+
when Constants::TaskExecution::ExecutionStatus::HAS_READY_STEPS
|
393
|
+
handle_ready_steps_state(task, context, synchronous, self)
|
394
|
+
# ... other states
|
395
|
+
end
|
396
|
+
end
|
397
|
+
```
|
398
|
+
|
399
|
+
### Workflow Lifecycle Integration
|
400
|
+
|
401
|
+
#### 1. **Task Finalization Decisions**
|
402
|
+
The TaskFinalizer uses execution context to make intelligent decisions:
|
403
|
+
|
404
|
+
```ruby
|
405
|
+
# lib/tasker/orchestration/task_finalizer.rb
|
406
|
+
class FinalizationDecisionMaker
|
407
|
+
def make_finalization_decision(task, context, synchronous, finalizer)
|
408
|
+
case context.execution_status
|
409
|
+
when 'has_ready_steps'
|
410
|
+
# Transition to in_progress and execute or reenqueue
|
411
|
+
when 'blocked_by_failures'
|
412
|
+
# Transition task to error state
|
413
|
+
when 'all_complete'
|
414
|
+
# Mark task as complete
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
418
|
+
```
|
419
|
+
|
420
|
+
#### 2. **Orchestration Coordination**
|
421
|
+
```ruby
|
422
|
+
# lib/tasker/orchestration/workflow_coordinator.rb
|
423
|
+
def execute_workflow(task, task_handler)
|
424
|
+
loop do
|
425
|
+
viable_steps = find_viable_steps(task, sequence, task_handler)
|
426
|
+
break if viable_steps.empty?
|
427
|
+
|
428
|
+
processed_steps = handle_viable_steps(task, sequence, viable_steps, task_handler)
|
429
|
+
|
430
|
+
break if blocked_by_errors?(task, sequence, processed_steps, task_handler)
|
431
|
+
end
|
432
|
+
|
433
|
+
finalize_task(task, sequence, processed_steps, task_handler)
|
434
|
+
end
|
435
|
+
```
|
436
|
+
|
437
|
+
#### 3. **Health Monitoring**
|
438
|
+
```ruby
|
439
|
+
# Example usage in monitoring/alerting
|
440
|
+
contexts = Tasker::Functions::FunctionBasedTaskExecutionContext.for_tasks(active_task_ids)
|
441
|
+
blocked_tasks = contexts.select { |ctx| ctx.health_status == 'blocked' }
|
442
|
+
```
|
443
|
+
|
444
|
+
---
|
445
|
+
|
446
|
+
## Function 4: `get_task_execution_contexts_batch`
|
447
|
+
|
448
|
+
### Purpose
|
449
|
+
Batch version of task execution context analysis, optimized for processing multiple tasks efficiently.
|
450
|
+
|
451
|
+
### Signature
|
452
|
+
```sql
|
453
|
+
get_task_execution_contexts_batch(input_task_ids BIGINT[])
|
454
|
+
```
|
455
|
+
|
456
|
+
### Key Differences from Single-Task Version
|
457
|
+
1. **Batch Step Data**: Uses `get_step_readiness_status_batch` internally
|
458
|
+
2. **Grouped Aggregation**: Statistics grouped by `task_id`
|
459
|
+
3. **Bulk Processing**: Single query for multiple tasks
|
460
|
+
|
461
|
+
### Logic Flow
|
462
|
+
```sql
|
463
|
+
WITH step_data AS (
|
464
|
+
SELECT * FROM get_step_readiness_status_batch(input_task_ids)
|
465
|
+
),
|
466
|
+
aggregated_stats AS (
|
467
|
+
SELECT
|
468
|
+
sd.task_id, -- GROUP BY task_id for batch processing
|
469
|
+
COUNT(*) as total_steps,
|
470
|
+
-- ... other aggregations
|
471
|
+
FROM step_data sd
|
472
|
+
GROUP BY sd.task_id
|
473
|
+
)
|
474
|
+
```
|
475
|
+
|
476
|
+
### Rails Integration
|
477
|
+
```ruby
|
478
|
+
# Efficient batch processing
|
479
|
+
task_ids = active_task_queue.pluck(:task_id)
|
480
|
+
contexts = Tasker::Functions::FunctionBasedTaskExecutionContext.for_tasks(task_ids)
|
481
|
+
|
482
|
+
# Process each context
|
483
|
+
contexts.each do |context|
|
484
|
+
case context.execution_status
|
485
|
+
when 'has_ready_steps'
|
486
|
+
enqueue_for_processing(context.task_id)
|
487
|
+
when 'blocked_by_failures'
|
488
|
+
alert_operations_team(context.task_id)
|
489
|
+
end
|
490
|
+
end
|
491
|
+
```
|
492
|
+
|
493
|
+
---
|
494
|
+
|
495
|
+
## Function 5: `calculate_dependency_levels`
|
496
|
+
|
497
|
+
### Purpose
|
498
|
+
Calculates the dependency level (depth from root nodes) for each workflow step in a task using recursive CTE traversal. This enables efficient dependency graph analysis, critical path identification, and parallelism optimization.
|
499
|
+
|
500
|
+
### Signature
|
501
|
+
```sql
|
502
|
+
calculate_dependency_levels(input_task_id BIGINT)
|
503
|
+
```
|
504
|
+
|
505
|
+
### Input Parameters
|
506
|
+
- `input_task_id`: The task ID to analyze dependency levels for
|
507
|
+
|
508
|
+
### Return Columns
|
509
|
+
| Column | Type | Description |
|
510
|
+
|--------|------|-------------|
|
511
|
+
| `workflow_step_id` | BIGINT | Unique step identifier |
|
512
|
+
| `dependency_level` | INTEGER | Depth from root nodes (0 = root, 1+ = depth) |
|
513
|
+
|
514
|
+
### Core Logic
|
515
|
+
|
516
|
+
#### 1. Recursive CTE Traversal
|
517
|
+
```sql
|
518
|
+
WITH RECURSIVE dependency_levels AS (
|
519
|
+
-- Base case: Find root nodes (steps with no dependencies)
|
520
|
+
SELECT
|
521
|
+
ws.workflow_step_id,
|
522
|
+
0 as level
|
523
|
+
FROM tasker_workflow_steps ws
|
524
|
+
WHERE ws.task_id = input_task_id
|
525
|
+
AND NOT EXISTS (
|
526
|
+
SELECT 1
|
527
|
+
FROM tasker_workflow_step_edges wse
|
528
|
+
WHERE wse.to_step_id = ws.workflow_step_id
|
529
|
+
)
|
530
|
+
|
531
|
+
UNION ALL
|
532
|
+
|
533
|
+
-- Recursive case: Find children of current level nodes
|
534
|
+
SELECT
|
535
|
+
wse.to_step_id as workflow_step_id,
|
536
|
+
dl.level + 1 as level
|
537
|
+
FROM dependency_levels dl
|
538
|
+
JOIN tasker_workflow_step_edges wse ON wse.from_step_id = dl.workflow_step_id
|
539
|
+
JOIN tasker_workflow_steps ws ON ws.workflow_step_id = wse.to_step_id
|
540
|
+
WHERE ws.task_id = input_task_id
|
541
|
+
)
|
542
|
+
```
|
543
|
+
|
544
|
+
#### 2. Multiple Path Handling
|
545
|
+
```sql
|
546
|
+
SELECT
|
547
|
+
dl.workflow_step_id,
|
548
|
+
MAX(dl.level) as dependency_level -- Use MAX to handle multiple paths to same node
|
549
|
+
FROM dependency_levels dl
|
550
|
+
GROUP BY dl.workflow_step_id
|
551
|
+
ORDER BY dependency_level, workflow_step_id;
|
552
|
+
```
|
553
|
+
|
554
|
+
**Key Features:**
|
555
|
+
- **Root Detection**: Identifies steps with no incoming edges (level 0)
|
556
|
+
- **Recursive Traversal**: Follows dependency edges to calculate depth
|
557
|
+
- **Multiple Path Resolution**: Uses MAX to handle convergent dependencies
|
558
|
+
- **Topological Ordering**: Results ordered by dependency level
|
559
|
+
|
560
|
+
### Performance Characteristics
|
561
|
+
|
562
|
+
#### Benchmarking Results
|
563
|
+
| Implementation | 10 Runs Performance | Improvement |
|
564
|
+
|---------------|-------------------|-------------|
|
565
|
+
| Ruby (Kahn's Algorithm) | 7.29ms | Baseline |
|
566
|
+
| SQL (Recursive CTE) | 6.04ms | **1.21x faster** |
|
567
|
+
| SQL (Recursive CTE) | 3.32ms | **2.46x faster** |
|
568
|
+
|
569
|
+
**Performance Benefits:**
|
570
|
+
- **Database-Native**: Leverages PostgreSQL's optimized recursive CTE engine
|
571
|
+
- **Single Query**: Eliminates multiple round-trips between Ruby and database
|
572
|
+
- **Index Optimized**: Uses existing indexes on workflow_step_edges table
|
573
|
+
- **Memory Efficient**: Processes dependency graph entirely in database memory
|
574
|
+
|
575
|
+
### Rails Integration
|
576
|
+
|
577
|
+
#### Primary Wrapper: `Tasker::Functions::FunctionBasedDependencyLevels`
|
578
|
+
|
579
|
+
```ruby
|
580
|
+
# Get dependency levels for all steps in a task
|
581
|
+
levels = Tasker::Functions::FunctionBasedDependencyLevels.for_task(task_id)
|
582
|
+
|
583
|
+
# Get levels as a hash (step_id => level)
|
584
|
+
levels_hash = Tasker::Functions::FunctionBasedDependencyLevels.levels_hash_for_task(task_id)
|
585
|
+
|
586
|
+
# Get maximum dependency level in task
|
587
|
+
max_level = Tasker::Functions::FunctionBasedDependencyLevels.max_level_for_task(task_id)
|
588
|
+
|
589
|
+
# Get all steps at a specific level
|
590
|
+
level_0_steps = Tasker::Functions::FunctionBasedDependencyLevels.steps_at_level(task_id, 0)
|
591
|
+
|
592
|
+
# Get root steps (level 0)
|
593
|
+
root_steps = Tasker::Functions::FunctionBasedDependencyLevels.root_steps_for_task(task_id)
|
594
|
+
```
|
595
|
+
|
596
|
+
#### Integration with RuntimeGraphAnalyzer
|
597
|
+
|
598
|
+
```ruby
|
599
|
+
# lib/tasker/analysis/runtime_graph_analyzer.rb
|
600
|
+
def build_dependency_graph
|
601
|
+
# Get dependency levels using SQL-based topological sort (faster than Ruby)
|
602
|
+
dependency_levels = calculate_dependency_levels_sql
|
603
|
+
|
604
|
+
{
|
605
|
+
nodes: steps.map do |step|
|
606
|
+
{
|
607
|
+
id: step.workflow_step_id,
|
608
|
+
name: step.named_step.name,
|
609
|
+
level: dependency_levels[step.workflow_step_id] || 0
|
610
|
+
}
|
611
|
+
end,
|
612
|
+
dependency_levels: dependency_levels
|
613
|
+
}
|
614
|
+
end
|
615
|
+
|
616
|
+
private
|
617
|
+
|
618
|
+
def calculate_dependency_levels_sql
|
619
|
+
Tasker::Functions::FunctionBasedDependencyLevels.levels_hash_for_task(task_id)
|
620
|
+
end
|
621
|
+
```
|
622
|
+
|
623
|
+
### Use Cases
|
624
|
+
|
625
|
+
#### 1. **Dependency Graph Analysis**
|
626
|
+
```ruby
|
627
|
+
# Analyze workflow structure
|
628
|
+
analyzer = Tasker::Analysis::RuntimeGraphAnalyzer.new(task: task)
|
629
|
+
graph = analyzer.analyze[:dependency_graph]
|
630
|
+
|
631
|
+
puts "Workflow has #{graph[:dependency_levels].values.max + 1} levels"
|
632
|
+
puts "Root steps: #{graph[:nodes].select { |n| n[:level] == 0 }.map { |n| n[:name] }}"
|
633
|
+
```
|
634
|
+
|
635
|
+
#### 2. **Critical Path Identification**
|
636
|
+
```ruby
|
637
|
+
# Find longest dependency chains
|
638
|
+
critical_paths = analyzer.analyze[:critical_paths]
|
639
|
+
puts "Longest path: #{critical_paths[:longest_path_length]} steps"
|
640
|
+
```
|
641
|
+
|
642
|
+
#### 3. **Parallelism Opportunities**
|
643
|
+
```ruby
|
644
|
+
# Identify steps that can run in parallel
|
645
|
+
levels_hash = Tasker::Functions::FunctionBasedDependencyLevels.levels_hash_for_task(task_id)
|
646
|
+
parallel_groups = levels_hash.group_by { |step_id, level| level }
|
647
|
+
|
648
|
+
parallel_groups.each do |level, steps|
|
649
|
+
puts "Level #{level}: #{steps.size} steps can run in parallel"
|
650
|
+
end
|
651
|
+
```
|
652
|
+
|
653
|
+
#### 4. **Workflow Validation**
|
654
|
+
```ruby
|
655
|
+
# Detect workflow complexity
|
656
|
+
max_level = Tasker::Functions::FunctionBasedDependencyLevels.max_level_for_task(task_id)
|
657
|
+
if max_level > 10
|
658
|
+
puts "Warning: Deep dependency chain detected (#{max_level} levels)"
|
659
|
+
end
|
660
|
+
```
|
661
|
+
|
662
|
+
### Migration Strategy
|
663
|
+
|
664
|
+
#### Function Deployment
|
665
|
+
```ruby
|
666
|
+
# db/migrate/20250616222419_add_calculate_dependency_levels_function.rb
|
667
|
+
def up
|
668
|
+
sql_file_path = Tasker::Engine.root.join('db', 'functions', 'calculate_dependency_levels_v01.sql')
|
669
|
+
execute File.read(sql_file_path)
|
670
|
+
end
|
671
|
+
|
672
|
+
def down
|
673
|
+
execute 'DROP FUNCTION IF EXISTS calculate_dependency_levels(BIGINT);'
|
674
|
+
end
|
675
|
+
```
|
676
|
+
|
677
|
+
#### Validation Results ✅
|
678
|
+
- ✅ **Ruby vs SQL Consistency**: Both implementations produce identical results
|
679
|
+
- ✅ **Complex Workflow Testing**: All workflow patterns (linear, diamond, tree, parallel merge, mixed) validated
|
680
|
+
- ✅ **Performance Benchmarking**: SQL consistently 1.2-2.5x faster
|
681
|
+
- ✅ **Integration Testing**: RuntimeGraphAnalyzer integration working correctly
|
682
|
+
|
683
|
+
---
|
684
|
+
|
685
|
+
## Function 6: `get_analytics_metrics_v01`
|
686
|
+
|
687
|
+
### Purpose
|
688
|
+
Provides comprehensive system-wide analytics metrics for performance monitoring, including system overview, performance metrics, and duration calculations. Optimized for real-time dashboard and analytics endpoints.
|
689
|
+
|
690
|
+
### Signature
|
691
|
+
```sql
|
692
|
+
get_analytics_metrics_v01(since_timestamp TIMESTAMPTZ DEFAULT NOW() - INTERVAL '1 hour')
|
693
|
+
```
|
694
|
+
|
695
|
+
### Input Parameters
|
696
|
+
- `since_timestamp`: Start time for analysis (defaults to 1 hour ago)
|
697
|
+
|
698
|
+
### Return Columns
|
699
|
+
| Column | Type | Description |
|
700
|
+
|--------|------|-------------|
|
701
|
+
| `active_tasks_count` | INTEGER | Number of currently active tasks |
|
702
|
+
| `total_namespaces_count` | INTEGER | Total number of task namespaces |
|
703
|
+
| `unique_task_types_count` | INTEGER | Number of distinct task types |
|
704
|
+
| `system_health_score` | DECIMAL | Health score based on recent performance (0.0-1.0) |
|
705
|
+
| `task_throughput` | INTEGER | Tasks created since timestamp |
|
706
|
+
| `completion_count` | INTEGER | Tasks completed since timestamp |
|
707
|
+
| `error_count` | INTEGER | Tasks that failed since timestamp |
|
708
|
+
| `completion_rate` | DECIMAL | Percentage of tasks completed (0.0-100.0) |
|
709
|
+
| `error_rate` | DECIMAL | Percentage of tasks failed (0.0-100.0) |
|
710
|
+
| `avg_task_duration` | DECIMAL | Average task duration in seconds |
|
711
|
+
| `avg_step_duration` | DECIMAL | Average step duration in seconds |
|
712
|
+
| `step_throughput` | INTEGER | Total steps processed since timestamp |
|
713
|
+
| `analysis_period_start` | TEXT | Start time of analysis period |
|
714
|
+
| `calculated_at` | TEXT | When metrics were calculated |
|
715
|
+
|
716
|
+
### Core Logic
|
717
|
+
|
718
|
+
#### 1. System Overview Metrics
|
719
|
+
```sql
|
720
|
+
-- Count currently active tasks (in_progress status)
|
721
|
+
active_tasks_count = (
|
722
|
+
SELECT COUNT(*)
|
723
|
+
FROM tasker_tasks t
|
724
|
+
LEFT JOIN tasker_task_transitions tt ON tt.task_id = t.task_id AND tt.most_recent = true
|
725
|
+
WHERE COALESCE(tt.to_state, 'pending') = 'in_progress'
|
726
|
+
)
|
727
|
+
|
728
|
+
-- Total namespaces and unique task types
|
729
|
+
total_namespaces_count = (SELECT COUNT(*) FROM tasker_task_namespaces)
|
730
|
+
unique_task_types_count = (SELECT COUNT(DISTINCT nt.name) FROM tasker_named_tasks nt)
|
731
|
+
```
|
732
|
+
|
733
|
+
#### 2. Performance Metrics Since Timestamp
|
734
|
+
```sql
|
735
|
+
-- Task throughput and completion analysis
|
736
|
+
task_throughput = (SELECT COUNT(*) FROM tasker_tasks WHERE created_at >= since_timestamp)
|
737
|
+
completion_count = (
|
738
|
+
SELECT COUNT(DISTINCT t.task_id)
|
739
|
+
FROM tasker_tasks t
|
740
|
+
JOIN tasker_task_transitions tt ON tt.task_id = t.task_id AND tt.most_recent = true
|
741
|
+
WHERE t.created_at >= since_timestamp AND tt.to_state = 'complete'
|
742
|
+
)
|
743
|
+
```
|
744
|
+
|
745
|
+
#### 3. Health Score Calculation
|
746
|
+
```sql
|
747
|
+
-- System health based on recent failure rate
|
748
|
+
system_health_score = CASE
|
749
|
+
WHEN task_throughput = 0 THEN 1.0
|
750
|
+
ELSE GREATEST(0.0, LEAST(1.0, 1.0 - (error_count::DECIMAL / task_throughput)))
|
751
|
+
END
|
752
|
+
```
|
753
|
+
|
754
|
+
### Performance Characteristics
|
755
|
+
- **Execution Time**: <5ms for typical workloads
|
756
|
+
- **Index Utilization**: Leverages task creation and transition indexes
|
757
|
+
- **Memory Efficiency**: Single-pass aggregation with minimal memory footprint
|
758
|
+
|
759
|
+
### Rails Integration
|
760
|
+
|
761
|
+
```ruby
|
762
|
+
# lib/tasker/functions/function_based_analytics_metrics.rb
|
763
|
+
metrics = Tasker::Functions::FunctionBasedAnalyticsMetrics.call(1.hour.ago)
|
764
|
+
|
765
|
+
puts "System Health Score: #{metrics.system_health_score}"
|
766
|
+
puts "Completion Rate: #{metrics.completion_rate}%"
|
767
|
+
puts "Active Tasks: #{metrics.active_tasks_count}"
|
768
|
+
```
|
769
|
+
|
770
|
+
---
|
771
|
+
|
772
|
+
## Function 7: `get_slowest_tasks_v01`
|
773
|
+
|
774
|
+
### Purpose
|
775
|
+
Identifies the slowest-performing tasks within a specified time period with comprehensive filtering capabilities. Essential for bottleneck analysis and performance optimization.
|
776
|
+
|
777
|
+
### Signature
|
778
|
+
```sql
|
779
|
+
get_slowest_tasks_v01(
|
780
|
+
since_timestamp TIMESTAMPTZ DEFAULT NOW() - INTERVAL '24 hours',
|
781
|
+
limit_count INTEGER DEFAULT 10,
|
782
|
+
namespace_filter VARCHAR(255) DEFAULT NULL,
|
783
|
+
task_name_filter VARCHAR(255) DEFAULT NULL,
|
784
|
+
version_filter VARCHAR(255) DEFAULT NULL
|
785
|
+
)
|
786
|
+
```
|
787
|
+
|
788
|
+
### Input Parameters
|
789
|
+
- `since_timestamp`: Start time for analysis (defaults to 24 hours ago)
|
790
|
+
- `limit_count`: Maximum number of results to return (default: 10)
|
791
|
+
- `namespace_filter`: Filter by namespace name (optional)
|
792
|
+
- `task_name_filter`: Filter by task name (optional)
|
793
|
+
- `version_filter`: Filter by task version (optional)
|
794
|
+
|
795
|
+
### Return Columns
|
796
|
+
| Column | Type | Description |
|
797
|
+
|--------|------|-------------|
|
798
|
+
| `task_id` | BIGINT | Unique task identifier |
|
799
|
+
| `task_name` | VARCHAR | Name of the task type |
|
800
|
+
| `namespace_name` | VARCHAR | Task namespace |
|
801
|
+
| `version` | VARCHAR | Task version |
|
802
|
+
| `duration_seconds` | DECIMAL | Total task duration in seconds |
|
803
|
+
| `step_count` | INTEGER | Total number of steps in task |
|
804
|
+
| `completed_steps` | INTEGER | Number of completed steps |
|
805
|
+
| `error_steps` | INTEGER | Number of failed steps |
|
806
|
+
| `created_at` | TIMESTAMPTZ | When task was created |
|
807
|
+
| `completed_at` | TIMESTAMPTZ | When task completed (NULL if still running) |
|
808
|
+
| `initiator` | VARCHAR | Who/what initiated the task |
|
809
|
+
| `source_system` | VARCHAR | Source system identifier |
|
810
|
+
|
811
|
+
### Core Logic
|
812
|
+
|
813
|
+
#### 1. Task Duration Calculation
|
814
|
+
```sql
|
815
|
+
-- Calculate duration from creation to completion or current time
|
816
|
+
duration_seconds = CASE
|
817
|
+
WHEN task_transitions.to_state = 'complete' AND task_transitions.most_recent = true THEN
|
818
|
+
EXTRACT(EPOCH FROM (task_transitions.created_at - t.created_at))
|
819
|
+
ELSE
|
820
|
+
EXTRACT(EPOCH FROM (NOW() - t.created_at))
|
821
|
+
END
|
822
|
+
```
|
823
|
+
|
824
|
+
#### 2. Step Aggregation
|
825
|
+
```sql
|
826
|
+
-- Count steps by status using most recent transitions
|
827
|
+
step_count = COUNT(ws.workflow_step_id)
|
828
|
+
completed_steps = COUNT(CASE
|
829
|
+
WHEN wst.to_state IN ('complete', 'resolved_manually') AND wst.most_recent = true
|
830
|
+
THEN 1
|
831
|
+
END)
|
832
|
+
error_steps = COUNT(CASE
|
833
|
+
WHEN wst.to_state = 'error' AND wst.most_recent = true
|
834
|
+
THEN 1
|
835
|
+
END)
|
836
|
+
```
|
837
|
+
|
838
|
+
#### 3. Filtering Logic
|
839
|
+
```sql
|
840
|
+
WHERE t.created_at >= since_timestamp
|
841
|
+
AND (namespace_filter IS NULL OR tn.name = namespace_filter)
|
842
|
+
AND (task_name_filter IS NULL OR nt.name = task_name_filter)
|
843
|
+
AND (version_filter IS NULL OR nt.version = version_filter)
|
844
|
+
ORDER BY duration_seconds DESC
|
845
|
+
LIMIT limit_count
|
846
|
+
```
|
847
|
+
|
848
|
+
### Rails Integration
|
849
|
+
|
850
|
+
```ruby
|
851
|
+
# lib/tasker/functions/function_based_slowest_tasks.rb
|
852
|
+
slowest_tasks = Tasker::Functions::FunctionBasedSlowestTasks.call(
|
853
|
+
since_timestamp: 24.hours.ago,
|
854
|
+
limit_count: 5,
|
855
|
+
namespace_filter: 'payments'
|
856
|
+
)
|
857
|
+
|
858
|
+
slowest_tasks.each do |task|
|
859
|
+
puts "#{task.task_name}: #{task.duration_seconds}s (#{task.completed_steps}/#{task.step_count} steps)"
|
860
|
+
end
|
861
|
+
```
|
862
|
+
|
863
|
+
---
|
864
|
+
|
865
|
+
## Function 8: `get_slowest_steps_v01`
|
866
|
+
|
867
|
+
### Purpose
|
868
|
+
Analyzes individual workflow step performance to identify bottlenecks at the step level. Provides detailed timing information for performance optimization and troubleshooting.
|
869
|
+
|
870
|
+
### Signature
|
871
|
+
```sql
|
872
|
+
get_slowest_steps_v01(
|
873
|
+
since_timestamp TIMESTAMPTZ DEFAULT NOW() - INTERVAL '24 hours',
|
874
|
+
limit_count INTEGER DEFAULT 10,
|
875
|
+
namespace_filter VARCHAR(255) DEFAULT NULL,
|
876
|
+
task_name_filter VARCHAR(255) DEFAULT NULL,
|
877
|
+
version_filter VARCHAR(255) DEFAULT NULL
|
878
|
+
)
|
879
|
+
```
|
880
|
+
|
881
|
+
### Input Parameters
|
882
|
+
- `since_timestamp`: Start time for analysis (defaults to 24 hours ago)
|
883
|
+
- `limit_count`: Maximum number of results to return (default: 10)
|
884
|
+
- `namespace_filter`: Filter by namespace name (optional)
|
885
|
+
- `task_name_filter`: Filter by task name (optional)
|
886
|
+
- `version_filter`: Filter by task version (optional)
|
887
|
+
|
888
|
+
### Return Columns
|
889
|
+
| Column | Type | Description |
|
890
|
+
|--------|------|-------------|
|
891
|
+
| `workflow_step_id` | BIGINT | Unique step identifier |
|
892
|
+
| `task_id` | BIGINT | Parent task identifier |
|
893
|
+
| `step_name` | VARCHAR | Name of the step |
|
894
|
+
| `task_name` | VARCHAR | Name of the parent task |
|
895
|
+
| `namespace_name` | VARCHAR | Task namespace |
|
896
|
+
| `version` | VARCHAR | Task version |
|
897
|
+
| `duration_seconds` | DECIMAL | Step execution duration in seconds |
|
898
|
+
| `attempts` | INTEGER | Number of execution attempts |
|
899
|
+
| `created_at` | TIMESTAMPTZ | When step was created |
|
900
|
+
| `completed_at` | TIMESTAMPTZ | When step completed |
|
901
|
+
| `retryable` | BOOLEAN | Whether step allows retries |
|
902
|
+
| `step_status` | VARCHAR | Current step status |
|
903
|
+
|
904
|
+
### Core Logic
|
905
|
+
|
906
|
+
#### 1. Step Duration Calculation
|
907
|
+
```sql
|
908
|
+
-- Calculate actual execution time from in_progress to complete
|
909
|
+
duration_seconds = CASE
|
910
|
+
WHEN complete_transition.created_at IS NOT NULL AND start_transition.created_at IS NOT NULL THEN
|
911
|
+
EXTRACT(EPOCH FROM (complete_transition.created_at - start_transition.created_at))
|
912
|
+
ELSE 0.0
|
913
|
+
END
|
914
|
+
```
|
915
|
+
|
916
|
+
#### 2. Status and Retry Information
|
917
|
+
```sql
|
918
|
+
-- Get current step status and retry eligibility
|
919
|
+
step_status = COALESCE(current_transition.to_state, 'pending')
|
920
|
+
retryable = COALESCE(ws.retryable, true)
|
921
|
+
attempts = COALESCE(ws.attempts, 0)
|
922
|
+
```
|
923
|
+
|
924
|
+
#### 3. Multi-table Filtering
|
925
|
+
```sql
|
926
|
+
-- Join through task relationships for comprehensive filtering
|
927
|
+
FROM tasker_workflow_steps ws
|
928
|
+
JOIN tasker_tasks t ON t.task_id = ws.task_id
|
929
|
+
JOIN tasker_named_tasks nt ON nt.named_task_id = t.named_task_id
|
930
|
+
JOIN tasker_task_namespaces tn ON tn.task_namespace_id = nt.task_namespace_id
|
931
|
+
WHERE ws.created_at >= since_timestamp
|
932
|
+
AND complete_transition.to_state IN ('complete', 'resolved_manually')
|
933
|
+
-- Apply filters...
|
934
|
+
ORDER BY duration_seconds DESC
|
935
|
+
```
|
936
|
+
|
937
|
+
### Performance Optimization
|
938
|
+
- **Index Strategy**: Uses compound indexes on (task_id, step_id, created_at)
|
939
|
+
- **Transition Filtering**: Only considers completed steps for accurate timing
|
940
|
+
- **Efficient Joins**: Optimized join order for minimal scan cost
|
941
|
+
|
942
|
+
### Rails Integration
|
943
|
+
|
944
|
+
```ruby
|
945
|
+
# lib/tasker/functions/function_based_slowest_steps.rb
|
946
|
+
slowest_steps = Tasker::Functions::FunctionBasedSlowestSteps.call(
|
947
|
+
since_timestamp: 4.hours.ago,
|
948
|
+
limit_count: 15,
|
949
|
+
namespace_filter: 'inventory'
|
950
|
+
)
|
951
|
+
|
952
|
+
slowest_steps.each do |step|
|
953
|
+
puts "#{step.step_name} (#{step.task_name}): #{step.duration_seconds}s - #{step.attempts} attempts"
|
954
|
+
end
|
955
|
+
```
|
956
|
+
|
957
|
+
### Use Cases
|
958
|
+
|
959
|
+
#### 1. **Bottleneck Identification**
|
960
|
+
```ruby
|
961
|
+
# Find consistently slow steps across tasks
|
962
|
+
slow_steps = Tasker::Functions::FunctionBasedSlowestSteps.call(limit_count: 50)
|
963
|
+
bottlenecks = slow_steps.group_by(&:step_name)
|
964
|
+
.select { |name, steps| steps.size > 5 }
|
965
|
+
.map { |name, steps| [name, steps.map(&:duration_seconds).sum / steps.size] }
|
966
|
+
```
|
967
|
+
|
968
|
+
#### 2. **Performance Regression Detection**
|
969
|
+
```ruby
|
970
|
+
# Compare current vs historical performance
|
971
|
+
current_avg = recent_steps.map(&:duration_seconds).sum / recent_steps.size
|
972
|
+
historical_avg = historical_steps.map(&:duration_seconds).sum / historical_steps.size
|
973
|
+
regression_ratio = current_avg / historical_avg
|
974
|
+
```
|
975
|
+
|
976
|
+
#### 3. **Retry Pattern Analysis**
|
977
|
+
```ruby
|
978
|
+
# Analyze retry patterns for problematic steps
|
979
|
+
retry_analysis = slow_steps.group_by(&:step_name)
|
980
|
+
.map { |name, steps| [name, steps.map(&:attempts).max] }
|
981
|
+
.select { |name, max_attempts| max_attempts > 2 }
|
982
|
+
```
|
983
|
+
|
984
|
+
---
|
985
|
+
|
986
|
+
## Performance Characteristics
|
987
|
+
|
988
|
+
### Query Optimization Techniques
|
989
|
+
|
990
|
+
#### 1. **Index-Optimized Joins**
|
991
|
+
- Uses `most_recent = true` flag instead of `DISTINCT ON` or window functions
|
992
|
+
- Direct joins instead of correlated subqueries
|
993
|
+
- Leverages primary key indexes for fast lookups
|
994
|
+
|
995
|
+
#### 2. **Selective Filtering**
|
996
|
+
```sql
|
997
|
+
-- Filter by task first (highly selective)
|
998
|
+
WHERE ws.task_id = input_task_id
|
999
|
+
-- Then optionally by steps (if provided)
|
1000
|
+
AND (step_ids IS NULL OR ws.workflow_step_id = ANY(step_ids))
|
1001
|
+
```
|
1002
|
+
|
1003
|
+
#### 3. **Efficient Aggregation**
|
1004
|
+
- Uses `COUNT(CASE WHEN ... THEN 1 END)` instead of multiple subqueries
|
1005
|
+
- Single-pass aggregation with conditional counting
|
1006
|
+
- Minimal memory footprint
|
1007
|
+
|
1008
|
+
### Performance Benefits
|
1009
|
+
|
1010
|
+
| Operation | Old View Approach | New Function Approach | Improvement |
|
1011
|
+
|-----------|------------------|----------------------|-------------|
|
1012
|
+
| Single Task Analysis | O(n) joins per task | O(1) optimized query | 5-10x faster |
|
1013
|
+
| Batch Processing | N queries (N+1 problem) | Single batch query | 10-50x faster |
|
1014
|
+
| Dependency Checking | Recursive subqueries | Direct join counting | 3-5x faster |
|
1015
|
+
| State Transitions | Multiple DISTINCT ON | Indexed flag lookup | 2-3x faster |
|
1016
|
+
| Analytics Metrics | Multiple controller queries | Single SQL function | 8-15x faster |
|
1017
|
+
| Bottleneck Analysis | Complex ActiveRecord chains | Optimized task/step functions | 5-12x faster |
|
1018
|
+
|
1019
|
+
---
|
1020
|
+
|
1021
|
+
## Critical Bug Fixes in SQL Functions
|
1022
|
+
|
1023
|
+
### SQL Function Backoff Logic Bug - CRITICAL FIX ✅
|
1024
|
+
**Issue**: SQL function backoff logic was incorrectly implemented using OR conditions
|
1025
|
+
**Problem**: Steps in active backoff were being marked as ready for execution, causing race conditions
|
1026
|
+
**Impact**: **CRITICAL** - This broke core workflow correctness and could cause duplicate processing
|
1027
|
+
|
1028
|
+
**Root Cause**: Incorrect boolean logic in backoff timing calculation:
|
1029
|
+
```sql
|
1030
|
+
-- BEFORE (broken logic):
|
1031
|
+
(ws.backoff_request_seconds IS NULL OR ws.last_attempted_at IS NULL OR
|
1032
|
+
ws.last_attempted_at + (ws.backoff_request_seconds * interval '1 second') <= NOW())
|
1033
|
+
-- This would return TRUE when backoff was active, making step ready incorrectly
|
1034
|
+
|
1035
|
+
-- AFTER (correct logic):
|
1036
|
+
CASE
|
1037
|
+
WHEN ws.backoff_request_seconds IS NOT NULL AND ws.last_attempted_at IS NOT NULL THEN
|
1038
|
+
ws.last_attempted_at + (ws.backoff_request_seconds * interval '1 second') <= NOW()
|
1039
|
+
ELSE true -- No explicit backoff set
|
1040
|
+
END
|
1041
|
+
-- This correctly returns FALSE when backoff is active, preventing premature execution
|
1042
|
+
```
|
1043
|
+
|
1044
|
+
**Fix Applied**: Replaced OR-based logic with explicit CASE statement that properly handles backoff timing
|
1045
|
+
**Validation**: Backoff test now passes - steps in backoff are correctly excluded from execution
|
1046
|
+
**Files Fixed**: `db/functions/get_step_readiness_status_v01.sql`, `db/functions/get_step_readiness_status_batch_v01.sql`
|
1047
|
+
|
1048
|
+
### State Machine Integration Fixes ✅
|
1049
|
+
**Issue**: TaskStateMachine and StepStateMachine were using Statesman's default `current_state` method, but custom transition models don't include `Statesman::Adapters::ActiveRecordTransition`
|
1050
|
+
**Problem**: State machine queries returning incorrect states
|
1051
|
+
**Symptom**: Tasks showing as `error` status even when most recent transition was `complete`
|
1052
|
+
|
1053
|
+
**Fixes Applied**:
|
1054
|
+
```ruby
|
1055
|
+
# TaskStateMachine.current_state Override
|
1056
|
+
def current_state
|
1057
|
+
most_recent_transition = object.task_transitions.where(most_recent: true).first
|
1058
|
+
if most_recent_transition
|
1059
|
+
most_recent_transition.to_state
|
1060
|
+
else
|
1061
|
+
Constants::TaskStatuses::PENDING
|
1062
|
+
end
|
1063
|
+
end
|
1064
|
+
|
1065
|
+
# StepStateMachine.current_state Override
|
1066
|
+
def current_state
|
1067
|
+
most_recent_transition = object.workflow_step_transitions.where(most_recent: true).first
|
1068
|
+
if most_recent_transition
|
1069
|
+
most_recent_transition.to_state
|
1070
|
+
else
|
1071
|
+
Constants::WorkflowStepStatuses::PENDING
|
1072
|
+
end
|
1073
|
+
end
|
1074
|
+
```
|
1075
|
+
|
1076
|
+
### Processing Flag Management ✅
|
1077
|
+
**Issue**: StepExecutor wasn't properly setting processing flags after step completion
|
1078
|
+
**Solution**: Enhanced StepExecutor to properly manage step flags:
|
1079
|
+
```ruby
|
1080
|
+
# In StepExecutor.complete_step_execution
|
1081
|
+
step.processed = true
|
1082
|
+
step.in_process = false
|
1083
|
+
step.processed_at = Time.zone.now
|
1084
|
+
```
|
1085
|
+
|
1086
|
+
---
|
1087
|
+
|
1088
|
+
## Error Handling and Edge Cases
|
1089
|
+
|
1090
|
+
### Function Robustness
|
1091
|
+
|
1092
|
+
#### 1. **Missing Data Handling**
|
1093
|
+
```sql
|
1094
|
+
-- Graceful defaults for missing data
|
1095
|
+
COALESCE(current_state.to_state, 'pending')::TEXT as current_state
|
1096
|
+
COALESCE(ws.retry_limit, 3) as retry_limit
|
1097
|
+
COALESCE(ws.retryable, true) = true
|
1098
|
+
```
|
1099
|
+
|
1100
|
+
#### 2. **Empty Result Sets**
|
1101
|
+
- Functions return empty result sets (not errors) for non-existent tasks
|
1102
|
+
- Aggregation functions handle zero-row inputs correctly
|
1103
|
+
- Rails wrappers handle `nil` contexts gracefully
|
1104
|
+
|
1105
|
+
#### 3. **State Consistency**
|
1106
|
+
- Functions use consistent transaction isolation
|
1107
|
+
- `most_recent = true` flag ensures consistent state views
|
1108
|
+
- No race conditions between state transitions and readiness checks
|
1109
|
+
|
1110
|
+
### Common Pitfalls and Solutions
|
1111
|
+
|
1112
|
+
#### 1. **Processed Flag Confusion**
|
1113
|
+
```sql
|
1114
|
+
-- CRITICAL: Only unprocessed steps can be ready
|
1115
|
+
AND (ws.processed = false OR ws.processed IS NULL)
|
1116
|
+
```
|
1117
|
+
**Pitfall**: Forgetting to check `processed = false` can cause re-execution of completed steps.
|
1118
|
+
|
1119
|
+
#### 2. **Retry vs Permanent Failure**
|
1120
|
+
```sql
|
1121
|
+
-- Only count truly blocked failures
|
1122
|
+
COUNT(CASE WHEN sd.current_state = 'error' AND (sd.attempts >= sd.retry_limit) THEN 1 END) as permanently_blocked_steps
|
1123
|
+
```
|
1124
|
+
**Pitfall**: Treating retry-eligible failures as permanent blocks disrupts exponential backoff.
|
1125
|
+
|
1126
|
+
#### 3. **Dependency Satisfaction Logic**
|
1127
|
+
```sql
|
1128
|
+
-- Handle steps with no dependencies (root steps)
|
1129
|
+
WHEN dep_edges.to_step_id IS NULL THEN true -- Root steps
|
1130
|
+
WHEN COUNT(dep_edges.from_step_id) = 0 THEN true -- Zero dependencies
|
1131
|
+
```
|
1132
|
+
**Pitfall**: Root steps must be explicitly handled or they'll never be marked as ready.
|
1133
|
+
|
1134
|
+
---
|
1135
|
+
|
1136
|
+
## Migration and Deployment
|
1137
|
+
|
1138
|
+
### Database Migration Files
|
1139
|
+
|
1140
|
+
**SQL Functions Created:**
|
1141
|
+
1. **`db/migrate/20250612000004_create_step_readiness_function.rb`**
|
1142
|
+
- Creates `get_step_readiness_status` function
|
1143
|
+
- Loads from `db/functions/get_step_readiness_status_v01.sql`
|
1144
|
+
|
1145
|
+
2. **`db/migrate/20250612000005_create_task_execution_context_function.rb`**
|
1146
|
+
- Creates `get_task_execution_context` function
|
1147
|
+
- Loads from `db/functions/get_task_execution_context_v01.sql`
|
1148
|
+
|
1149
|
+
3. **`db/migrate/20250612000006_create_batch_task_execution_context_function.rb`**
|
1150
|
+
- Creates `get_task_execution_contexts_batch` function
|
1151
|
+
- Loads from `db/functions/get_task_execution_contexts_batch_v01.sql`
|
1152
|
+
|
1153
|
+
4. **`db/migrate/20250612000007_create_batch_step_readiness_function.rb`**
|
1154
|
+
- Creates `get_step_readiness_status_batch` function
|
1155
|
+
- Loads from `db/functions/get_step_readiness_status_batch_v01.sql`
|
1156
|
+
|
1157
|
+
5. **`db/migrate/20250616222419_add_calculate_dependency_levels_function.rb`**
|
1158
|
+
- Creates `calculate_dependency_levels` function
|
1159
|
+
- Loads from `db/functions/calculate_dependency_levels_v01.sql`
|
1160
|
+
|
1161
|
+
**Analytics Functions Added in v2.7.0:**
|
1162
|
+
|
1163
|
+
6. **`get_analytics_metrics_v01`** (Via system migrations)
|
1164
|
+
- Comprehensive system metrics aggregation
|
1165
|
+
- Supports performance monitoring and health scoring
|
1166
|
+
|
1167
|
+
7. **`get_slowest_tasks_v01`** (Via system migrations)
|
1168
|
+
- Task-level performance analysis with filtering
|
1169
|
+
- Essential for bottleneck identification
|
1170
|
+
|
1171
|
+
8. **`get_slowest_steps_v01`** (Via system migrations)
|
1172
|
+
- Step-level performance analysis
|
1173
|
+
- Detailed execution timing and retry pattern analysis
|
1174
|
+
|
1175
|
+
**Function Wrapper Classes Created:**
|
1176
|
+
- `lib/tasker/functions/function_based_step_readiness_status.rb` - Step readiness function wrapper
|
1177
|
+
- `lib/tasker/functions/function_based_task_execution_context.rb` - Task context function wrapper
|
1178
|
+
- `lib/tasker/functions/function_based_dependency_levels.rb` - Dependency levels function wrapper
|
1179
|
+
- `lib/tasker/functions/function_based_analytics_metrics.rb` - Analytics metrics function wrapper (v2.7.0)
|
1180
|
+
- `lib/tasker/functions/function_based_slowest_tasks.rb` - Slowest tasks analysis function wrapper (v2.7.0)
|
1181
|
+
- `lib/tasker/functions/function_based_slowest_steps.rb` - Slowest steps analysis function wrapper (v2.7.0)
|
1182
|
+
- `lib/tasker/functions/function_wrapper.rb` - Base function wrapper class
|
1183
|
+
- `lib/tasker/functions.rb` - Function module loader
|
1184
|
+
|
1185
|
+
**ActiveRecord Models Updated:**
|
1186
|
+
- `app/models/tasker/step_readiness_status.rb` - Delegates to function-based implementation
|
1187
|
+
- `app/models/tasker/task_execution_context.rb` - Delegates to function-based implementation
|
1188
|
+
|
1189
|
+
### Deployment Strategy
|
1190
|
+
|
1191
|
+
**Status**: ✅ **COMPLETE AND PRODUCTION READY**
|
1192
|
+
|
1193
|
+
**Deployment Phases Completed:**
|
1194
|
+
1. ✅ **Index Optimizations**: Strategic database indexes implemented
|
1195
|
+
2. ✅ **SQL Functions**: High-performance functions deployed
|
1196
|
+
3. ✅ **State Machine Fixes**: Critical production stability fixes
|
1197
|
+
4. ✅ **ActiveRecord Models**: Function-based models deployed
|
1198
|
+
5. 🟡 **Legacy Cleanup**: Ready for implementation
|
1199
|
+
|
1200
|
+
**Zero-Downtime Deployment Achieved:**
|
1201
|
+
- Zero breaking changes, full backward compatibility maintained
|
1202
|
+
- Comprehensive rollback procedures implemented and tested
|
1203
|
+
- Performance monitoring and validation complete
|
1204
|
+
- All existing code continues to work unchanged
|
1205
|
+
|
1206
|
+
### Legacy Code Cleanup (Next Priority)
|
1207
|
+
|
1208
|
+
**Status**: 🟡 **HIGH PRIORITY - READY FOR IMPLEMENTATION**
|
1209
|
+
|
1210
|
+
With SQL functions complete, legacy database views can be removed:
|
1211
|
+
|
1212
|
+
**Files to Remove:**
|
1213
|
+
- `db/views/tasker_step_readiness_statuses_v01.sql` - **DELETE** (replaced by SQL functions)
|
1214
|
+
- `db/views/tasker_task_execution_contexts_v01.sql` - **DELETE** (replaced by SQL functions)
|
1215
|
+
- `db/views/tasker_active_step_readiness_statuses_v01.sql` - **DELETE** (replaced by SQL functions)
|
1216
|
+
- `db/views/tasker_active_task_execution_contexts_v01.sql` - **DELETE** (replaced by SQL functions)
|
1217
|
+
- `app/models/tasker/active_task_execution_context.rb` - **DELETE** (replaced by function-based models)
|
1218
|
+
- `app/models/tasker/active_step_readiness_status.rb` - **DELETE** (replaced by function-based models)
|
1219
|
+
|
1220
|
+
**Benefits of Cleanup:**
|
1221
|
+
- Remove 1000+ lines of unused database view code
|
1222
|
+
- Reduce complexity and improve maintainability
|
1223
|
+
- Single source of truth in SQL functions
|
1224
|
+
- Better performance through direct function calls
|
1225
|
+
|
1226
|
+
### Versioning Strategy
|
1227
|
+
- Functions use `_v01` suffix for versioning
|
1228
|
+
- Future changes increment version (e.g., `_v02`)
|
1229
|
+
- Migration can drop old versions after deployment validation
|
1230
|
+
|
1231
|
+
### Rollback Plan
|
1232
|
+
```ruby
|
1233
|
+
def down
|
1234
|
+
execute 'DROP FUNCTION IF EXISTS get_step_readiness_status(BIGINT, BIGINT[]);'
|
1235
|
+
execute 'DROP FUNCTION IF EXISTS get_step_readiness_status_batch(BIGINT[]);'
|
1236
|
+
execute 'DROP FUNCTION IF EXISTS get_task_execution_context(BIGINT);'
|
1237
|
+
execute 'DROP FUNCTION IF EXISTS get_task_execution_contexts_batch(BIGINT[]);'
|
1238
|
+
execute 'DROP FUNCTION IF EXISTS calculate_dependency_levels(BIGINT);'
|
1239
|
+
# Falls back to view-based implementation if still available
|
1240
|
+
end
|
1241
|
+
```
|
1242
|
+
|
1243
|
+
---
|
1244
|
+
|
1245
|
+
## Testing and Validation
|
1246
|
+
|
1247
|
+
### Function Testing Strategy
|
1248
|
+
|
1249
|
+
#### 1. **Unit Tests for SQL Logic**
|
1250
|
+
```ruby
|
1251
|
+
# spec/db/functions/sql_functions_integration_spec.rb
|
1252
|
+
describe 'get_step_readiness_status' do
|
1253
|
+
it 'correctly identifies ready steps' do
|
1254
|
+
results = execute_function("SELECT * FROM get_step_readiness_status(#{task.task_id})")
|
1255
|
+
ready_steps = results.select { |r| r['ready_for_execution'] }
|
1256
|
+
expect(ready_steps.size).to eq(expected_ready_count)
|
1257
|
+
end
|
1258
|
+
end
|
1259
|
+
```
|
1260
|
+
|
1261
|
+
#### 2. **Integration Tests with Rails Models**
|
1262
|
+
```ruby
|
1263
|
+
describe 'FunctionBasedStepReadinessStatus' do
|
1264
|
+
it 'provides same interface as original model' do
|
1265
|
+
result = Tasker::Functions::FunctionBasedStepReadinessStatus.for_task(task.task_id).first
|
1266
|
+
expect(result).to respond_to(:can_execute_now?)
|
1267
|
+
expect(result).to respond_to(:blocking_reason)
|
1268
|
+
end
|
1269
|
+
end
|
1270
|
+
```
|
1271
|
+
|
1272
|
+
#### 3. **Production Workflow Validation**
|
1273
|
+
```ruby
|
1274
|
+
describe 'Production Workflow Integration' do
|
1275
|
+
it 'completes complex workflows end-to-end' do
|
1276
|
+
task = create(:diamond_workflow_task)
|
1277
|
+
success = TestOrchestration::TestCoordinator.process_task_production_path(task)
|
1278
|
+
expect(success).to be true
|
1279
|
+
expect(task.reload.status).to eq('complete')
|
1280
|
+
end
|
1281
|
+
end
|
1282
|
+
```
|
1283
|
+
|
1284
|
+
### Performance Benchmarking
|
1285
|
+
|
1286
|
+
**Validated Performance Results:**
|
1287
|
+
- ✅ **Database timeouts eliminated** - No more 30+ second queries
|
1288
|
+
- ✅ **Enterprise scale validated** - 10,000+ concurrent tasks supported
|
1289
|
+
- ✅ **Functionality maintained** - All core features working correctly
|
1290
|
+
- ✅ **Backward compatibility** - No breaking changes to existing functionality
|
1291
|
+
|
1292
|
+
**Test Results Analysis:**
|
1293
|
+
- Individual step readiness: 0.013s (83 records)
|
1294
|
+
- Batch step readiness: 0.009s (83 records)
|
1295
|
+
- Functions vs views: 38% performance improvement
|
1296
|
+
- Task execution context: Individual=0.011s, Batch=0.008s
|
1297
|
+
|
1298
|
+
**Complex Workflow Testing (100% Success Rate):**
|
1299
|
+
- ✅ **LinearWorkflowTask**: Step1 → Step2 → Step3 → Step4 → Step5 → Step6
|
1300
|
+
- ✅ **DiamondWorkflowTask**: Start → (Branch1, Branch2) → Merge → End
|
1301
|
+
- ✅ **ParallelMergeWorkflowTask**: Multiple independent parallel branches that converge
|
1302
|
+
- ✅ **TreeWorkflowTask**: Root → (Branch A, Branch B) → (A1, A2, B1, B2) → Leaf processing
|
1303
|
+
- ✅ **MixedWorkflowTask**: Complex pattern with various dependency types
|
1304
|
+
|
1305
|
+
**Backoff Logic Validation:**
|
1306
|
+
The system correctly implements retry backoff logic:
|
1307
|
+
- **First failure**: 1-2 second backoff
|
1308
|
+
- **Second failure**: 2-4 second exponential backoff
|
1309
|
+
- **Third failure**: Up to 30 second maximum backoff
|
1310
|
+
This prevents retry storms and gives external systems time to recover.
|
1311
|
+
|
1312
|
+
---
|
1313
|
+
|
1314
|
+
## Monitoring and Observability
|
1315
|
+
|
1316
|
+
### Key Metrics to Track
|
1317
|
+
|
1318
|
+
#### 1. **Function Performance**
|
1319
|
+
- Query execution time
|
1320
|
+
- Result set sizes
|
1321
|
+
- Index hit ratios
|
1322
|
+
|
1323
|
+
#### 2. **Workflow Health**
|
1324
|
+
- Ready step counts over time
|
1325
|
+
- Blocked task percentages
|
1326
|
+
- Completion rates
|
1327
|
+
|
1328
|
+
#### 3. **Error Patterns**
|
1329
|
+
- Retry attempt distributions
|
1330
|
+
- Backoff timing effectiveness
|
1331
|
+
- Permanently blocked task reasons
|
1332
|
+
|
1333
|
+
### Alerting Thresholds
|
1334
|
+
- Function execution time > 100ms (investigate performance)
|
1335
|
+
- Blocked task percentage > 5% (workflow health issue)
|
1336
|
+
- Ready steps = 0 across all tasks (system stall)
|
1337
|
+
|
1338
|
+
---
|
1339
|
+
|
1340
|
+
## Future Enhancements
|
1341
|
+
|
1342
|
+
### Planned Improvements
|
1343
|
+
|
1344
|
+
#### 1. **Advanced Retry Strategies**
|
1345
|
+
- Jittered exponential backoff
|
1346
|
+
- Different backoff strategies per step type
|
1347
|
+
- Circuit breaker patterns
|
1348
|
+
|
1349
|
+
#### 2. **Performance Optimizations**
|
1350
|
+
- Materialized view caching for hot paths
|
1351
|
+
- Partitioning for large-scale deployments
|
1352
|
+
- Connection pooling optimizations
|
1353
|
+
|
1354
|
+
#### 3. **Enhanced Observability**
|
1355
|
+
- Real-time workflow state dashboards
|
1356
|
+
- Detailed execution time breakdowns
|
1357
|
+
- Predictive failure analysis
|
1358
|
+
|
1359
|
+
### API Stability
|
1360
|
+
- Function signatures are considered stable
|
1361
|
+
- New features will use optional parameters
|
1362
|
+
- Version increments for breaking changes
|
1363
|
+
|
1364
|
+
---
|
1365
|
+
|
1366
|
+
## Architecture Benefits
|
1367
|
+
|
1368
|
+
**Performance Excellence**: 25-100x improvements with sub-10ms operational queries via SQL functions
|
1369
|
+
**Ultra-High Performance**: SQL functions provide 4x better performance than database views
|
1370
|
+
**Maintainable**: Business logic in optimized SQL functions, Ruby provides clean interfaces
|
1371
|
+
**Scalable**: Performance scales with active workload, supports millions of historical tasks
|
1372
|
+
**Future-Proof**: Function-based architecture provides foundation for unlimited scale
|
1373
|
+
**Batch Optimized**: Batch operations provide maximum throughput for high-volume scenarios
|
1374
|
+
|
1375
|
+
## Current Status
|
1376
|
+
|
1377
|
+
**✅ SQL Function Optimization Complete**: The database performance optimization is **COMPLETE AND SUCCESSFUL**. SQL functions provide excellent performance with critical correctness fixes applied.
|
1378
|
+
|
1379
|
+
**✅ Performance Validated**:
|
1380
|
+
- 38% improvement over database views
|
1381
|
+
- Sub-10ms queries for individual operations
|
1382
|
+
- Batch operations 4x faster than individual calls
|
1383
|
+
- Backoff logic working correctly after critical fix
|
1384
|
+
|
1385
|
+
**✅ Correctness Validated**:
|
1386
|
+
- SQL functions accurately identify ready steps
|
1387
|
+
- Dependency resolution working for complex DAGs
|
1388
|
+
- Backoff timing correctly prevents premature execution
|
1389
|
+
- All SQL function integration tests passing
|
1390
|
+
|
1391
|
+
**✅ Production Ready**: Zero breaking changes, full backward compatibility maintained, comprehensive rollback procedures tested.
|
1392
|
+
|
1393
|
+
**🟡 Next Priority**: Legacy code cleanup to remove deprecated database views (estimated 2-4 hours).
|
1394
|
+
|
1395
|
+
## Summary
|
1396
|
+
|
1397
|
+
The Tasker SQL functions provide the high-performance foundation for intelligent workflow orchestration. By replacing expensive view-based queries with optimized stored procedures, they enable:
|
1398
|
+
|
1399
|
+
1. **Real-time Readiness Analysis**: O(1) step readiness calculations
|
1400
|
+
2. **Intelligent Task Coordination**: Context-aware finalization decisions
|
1401
|
+
3. **Batch Processing Efficiency**: Single queries for multiple tasks
|
1402
|
+
4. **Robust Retry Handling**: Proper distinction between temporary and permanent failures
|
1403
|
+
|
1404
|
+
These functions are critical components that make Tasker's concurrent workflow execution both performant and reliable at scale.
|
1405
|
+
|
1406
|
+
**Key Success Metric**: ✅ **ACHIEVED** - Active operational queries maintain <10ms performance regardless of historical task volume, solving the scalability concern that motivated this comprehensive optimization effort.
|
1407
|
+
|
1408
|
+
**The Tasker SQL function optimization is complete and production-ready for enterprise deployment.**
|