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.
Files changed (605) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +443 -0
  4. data/Rakefile +10 -0
  5. data/app/controllers/tasker/analytics_controller.rb +179 -0
  6. data/app/controllers/tasker/application_controller.rb +45 -0
  7. data/app/controllers/tasker/graphql_controller.rb +193 -0
  8. data/app/controllers/tasker/handlers_controller.rb +217 -0
  9. data/app/controllers/tasker/health_controller.rb +229 -0
  10. data/app/controllers/tasker/metrics_controller.rb +111 -0
  11. data/app/controllers/tasker/page_sort.rb +97 -0
  12. data/app/controllers/tasker/task_diagrams_controller.rb +30 -0
  13. data/app/controllers/tasker/tasks_controller.rb +123 -0
  14. data/app/controllers/tasker/workflow_steps_controller.rb +69 -0
  15. data/app/graphql/examples/all_tasks.graphql +22 -0
  16. data/app/graphql/examples/pending_tasks.graphql +23 -0
  17. data/app/graphql/tasker/graph_ql_types/annotation_type.rb +14 -0
  18. data/app/graphql/tasker/graph_ql_types/base_argument.rb +9 -0
  19. data/app/graphql/tasker/graph_ql_types/base_connection.rb +11 -0
  20. data/app/graphql/tasker/graph_ql_types/base_edge.rb +10 -0
  21. data/app/graphql/tasker/graph_ql_types/base_enum.rb +9 -0
  22. data/app/graphql/tasker/graph_ql_types/base_field.rb +10 -0
  23. data/app/graphql/tasker/graph_ql_types/base_input_object.rb +10 -0
  24. data/app/graphql/tasker/graph_ql_types/base_interface.rb +14 -0
  25. data/app/graphql/tasker/graph_ql_types/base_object.rb +10 -0
  26. data/app/graphql/tasker/graph_ql_types/base_scalar.rb +9 -0
  27. data/app/graphql/tasker/graph_ql_types/base_union.rb +11 -0
  28. data/app/graphql/tasker/graph_ql_types/dependent_system_object_map_type.rb +18 -0
  29. data/app/graphql/tasker/graph_ql_types/dependent_system_type.rb +13 -0
  30. data/app/graphql/tasker/graph_ql_types/mutation_type.rb +16 -0
  31. data/app/graphql/tasker/graph_ql_types/named_step_type.rb +16 -0
  32. data/app/graphql/tasker/graph_ql_types/named_task_type.rb +14 -0
  33. data/app/graphql/tasker/graph_ql_types/named_tasks_named_step_type.rb +19 -0
  34. data/app/graphql/tasker/graph_ql_types/node_type.rb +12 -0
  35. data/app/graphql/tasker/graph_ql_types/query_type.rb +20 -0
  36. data/app/graphql/tasker/graph_ql_types/task_annotation_type.rb +17 -0
  37. data/app/graphql/tasker/graph_ql_types/task_interface.rb +17 -0
  38. data/app/graphql/tasker/graph_ql_types/task_type.rb +26 -0
  39. data/app/graphql/tasker/graph_ql_types/workflow_step_type.rb +154 -0
  40. data/app/graphql/tasker/graph_ql_types.rb +42 -0
  41. data/app/graphql/tasker/mutations/base_mutation.rb +13 -0
  42. data/app/graphql/tasker/mutations/cancel_step.rb +29 -0
  43. data/app/graphql/tasker/mutations/cancel_task.rb +29 -0
  44. data/app/graphql/tasker/mutations/create_task.rb +52 -0
  45. data/app/graphql/tasker/mutations/update_step.rb +36 -0
  46. data/app/graphql/tasker/mutations/update_task.rb +41 -0
  47. data/app/graphql/tasker/queries/all_annotation_types.rb +17 -0
  48. data/app/graphql/tasker/queries/all_tasks.rb +23 -0
  49. data/app/graphql/tasker/queries/base_query.rb +9 -0
  50. data/app/graphql/tasker/queries/helpers.rb +16 -0
  51. data/app/graphql/tasker/queries/one_step.rb +24 -0
  52. data/app/graphql/tasker/queries/one_task.rb +18 -0
  53. data/app/graphql/tasker/queries/tasks_by_annotation.rb +31 -0
  54. data/app/graphql/tasker/queries/tasks_by_status.rb +30 -0
  55. data/app/graphql/tasker/tasker_rails_schema.rb +52 -0
  56. data/app/jobs/tasker/application_job.rb +8 -0
  57. data/app/jobs/tasker/metrics_export_job.rb +252 -0
  58. data/app/jobs/tasker/task_runner_job.rb +224 -0
  59. data/app/models/tasker/annotation_type.rb +26 -0
  60. data/app/models/tasker/application_record.rb +70 -0
  61. data/app/models/tasker/dependent_system.rb +26 -0
  62. data/app/models/tasker/dependent_system_object_map.rb +64 -0
  63. data/app/models/tasker/diagram/edge.rb +106 -0
  64. data/app/models/tasker/diagram/flowchart.rb +137 -0
  65. data/app/models/tasker/diagram/node.rb +99 -0
  66. data/app/models/tasker/named_step.rb +41 -0
  67. data/app/models/tasker/named_task.rb +121 -0
  68. data/app/models/tasker/named_tasks_named_step.rb +82 -0
  69. data/app/models/tasker/step_dag_relationship.rb +65 -0
  70. data/app/models/tasker/step_readiness_status.rb +59 -0
  71. data/app/models/tasker/task.rb +424 -0
  72. data/app/models/tasker/task_annotation.rb +36 -0
  73. data/app/models/tasker/task_diagram.rb +332 -0
  74. data/app/models/tasker/task_execution_context.rb +29 -0
  75. data/app/models/tasker/task_namespace.rb +41 -0
  76. data/app/models/tasker/task_transition.rb +235 -0
  77. data/app/models/tasker/workflow_step.rb +461 -0
  78. data/app/models/tasker/workflow_step_edge.rb +94 -0
  79. data/app/models/tasker/workflow_step_transition.rb +434 -0
  80. data/app/serializers/tasker/annotation_type_serializer.rb +8 -0
  81. data/app/serializers/tasker/handler_serializer.rb +109 -0
  82. data/app/serializers/tasker/task_annotation_serializer.rb +32 -0
  83. data/app/serializers/tasker/task_serializer.rb +168 -0
  84. data/app/serializers/tasker/workflow_step_serializer.rb +27 -0
  85. data/app/services/tasker/analytics_service.rb +409 -0
  86. data/app/views/tasker/task/_diagram.html.erb +32 -0
  87. data/config/initializers/dry_struct.rb +11 -0
  88. data/config/initializers/statesman.rb +6 -0
  89. data/config/initializers/tasker_orchestration.rb +17 -0
  90. data/config/initializers/time_formats.rb +4 -0
  91. data/config/routes.rb +34 -0
  92. data/config/tasker/subscriptions/example_integrations.yml +67 -0
  93. data/config/tasker/system_events.yml +305 -0
  94. data/db/functions/calculate_dependency_levels_v01.sql +45 -0
  95. data/db/functions/get_analytics_metrics_v01.sql +137 -0
  96. data/db/functions/get_slowest_steps_v01.sql +82 -0
  97. data/db/functions/get_slowest_tasks_v01.sql +96 -0
  98. data/db/functions/get_step_readiness_status_batch_v01.sql +140 -0
  99. data/db/functions/get_step_readiness_status_v01.sql +139 -0
  100. data/db/functions/get_system_health_counts_v01.sql +108 -0
  101. data/db/functions/get_task_execution_context_v01.sql +108 -0
  102. data/db/functions/get_task_execution_contexts_batch_v01.sql +104 -0
  103. data/db/init/schema.sql +2277 -0
  104. data/db/migrate/20250701165431_initial_tasker_schema.rb +116 -0
  105. data/db/views/tasker_step_dag_relationships_v01.sql +69 -0
  106. data/docs/APPLICATION_GENERATOR.md +384 -0
  107. data/docs/AUTH.md +1780 -0
  108. data/docs/CIRCUIT_BREAKER.md +224 -0
  109. data/docs/DEVELOPER_GUIDE.md +2665 -0
  110. data/docs/EVENT_SYSTEM.md +637 -0
  111. data/docs/EXECUTION_CONFIGURATION.md +341 -0
  112. data/docs/FLOW_CHART.md +149 -0
  113. data/docs/HEALTH.md +542 -0
  114. data/docs/METRICS.md +731 -0
  115. data/docs/OPTIMIZATION_PLAN.md +1479 -0
  116. data/docs/OVERVIEW.md +552 -0
  117. data/docs/QUICK_START.md +270 -0
  118. data/docs/REGISTRY_SYSTEMS.md +373 -0
  119. data/docs/REST_API.md +632 -0
  120. data/docs/ROADMAP.md +221 -0
  121. data/docs/SQL_FUNCTIONS.md +1408 -0
  122. data/docs/TASK_DIAGRAM.md +252 -0
  123. data/docs/TASK_EXECUTION_CONTROL_FLOW.md +237 -0
  124. data/docs/TELEMETRY.md +795 -0
  125. data/docs/TROUBLESHOOTING.md +756 -0
  126. data/docs/TaskHandlerGenerator.html +255 -0
  127. data/docs/Tasker/Analysis/RuntimeGraphAnalyzer.html +907 -0
  128. data/docs/Tasker/Analysis/TemplateGraphAnalyzer.html +1236 -0
  129. data/docs/Tasker/Analysis.html +117 -0
  130. data/docs/Tasker/AnalyticsController.html +450 -0
  131. data/docs/Tasker/AnalyticsService/BottleneckAnalytics.html +816 -0
  132. data/docs/Tasker/AnalyticsService/PerformanceAnalytics.html +586 -0
  133. data/docs/Tasker/AnalyticsService.html +2221 -0
  134. data/docs/Tasker/AnnotationType.html +137 -0
  135. data/docs/Tasker/AnnotationTypeSerializer.html +124 -0
  136. data/docs/Tasker/ApplicationController.html +147 -0
  137. data/docs/Tasker/ApplicationJob.html +128 -0
  138. data/docs/Tasker/ApplicationRecord.html +378 -0
  139. data/docs/Tasker/Authentication/AuthenticationError.html +124 -0
  140. data/docs/Tasker/Authentication/ConfigurationError.html +124 -0
  141. data/docs/Tasker/Authentication/Coordinator.html +242 -0
  142. data/docs/Tasker/Authentication/Interface.html +560 -0
  143. data/docs/Tasker/Authentication/InterfaceError.html +124 -0
  144. data/docs/Tasker/Authentication/NoneAuthenticator.html +338 -0
  145. data/docs/Tasker/Authentication.html +119 -0
  146. data/docs/Tasker/Authorization/AuthorizationError.html +139 -0
  147. data/docs/Tasker/Authorization/BaseCoordinator.html +927 -0
  148. data/docs/Tasker/Authorization/ConfigurationError.html +153 -0
  149. data/docs/Tasker/Authorization/ResourceConstants/ACTIONS.html +428 -0
  150. data/docs/Tasker/Authorization/ResourceConstants/RESOURCES.html +365 -0
  151. data/docs/Tasker/Authorization/ResourceConstants.html +146 -0
  152. data/docs/Tasker/Authorization/ResourceRegistry.html +882 -0
  153. data/docs/Tasker/Authorization/UnauthorizedError.html +153 -0
  154. data/docs/Tasker/Authorization.html +582 -0
  155. data/docs/Tasker/CacheCapabilities.html +167 -0
  156. data/docs/Tasker/CacheStrategy.html +1297 -0
  157. data/docs/Tasker/Concerns/Authenticatable.html +116 -0
  158. data/docs/Tasker/Concerns/Authorizable/AdminStatusChecker.html +256 -0
  159. data/docs/Tasker/Concerns/Authorizable.html +816 -0
  160. data/docs/Tasker/Concerns/ControllerAuthorizable.html +157 -0
  161. data/docs/Tasker/Concerns/EventPublisher.html +4023 -0
  162. data/docs/Tasker/Concerns/IdempotentStateTransitions.html +806 -0
  163. data/docs/Tasker/Concerns/LifecycleEventHelpers.html +129 -0
  164. data/docs/Tasker/Concerns/OrchestrationPublisher.html +129 -0
  165. data/docs/Tasker/Concerns/StateMachineBase/ClassMethods.html +1075 -0
  166. data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase/ClassMethods.html +191 -0
  167. data/docs/Tasker/Concerns/StateMachineBase/StateMachineBase.html +126 -0
  168. data/docs/Tasker/Concerns/StateMachineBase.html +153 -0
  169. data/docs/Tasker/Concerns/StructuredLogging.html +1413 -0
  170. data/docs/Tasker/Concerns.html +117 -0
  171. data/docs/Tasker/Configuration/AuthConfiguration.html +1023 -0
  172. data/docs/Tasker/Configuration/ConfigurationProxy.html +581 -0
  173. data/docs/Tasker/Configuration/DatabaseConfiguration.html +475 -0
  174. data/docs/Tasker/Configuration/EngineConfiguration.html +1265 -0
  175. data/docs/Tasker/Configuration/HealthConfiguration.html +791 -0
  176. data/docs/Tasker/Configuration/TelemetryConfiguration.html +1308 -0
  177. data/docs/Tasker/Configuration/TelemetryConfigurationProxy.html +388 -0
  178. data/docs/Tasker/Configuration.html +1669 -0
  179. data/docs/Tasker/ConfigurationError.html +143 -0
  180. data/docs/Tasker/ConfiguredTask.html +514 -0
  181. data/docs/Tasker/Constants/EventDefinitions.html +590 -0
  182. data/docs/Tasker/Constants/LifecycleEvents.html +137 -0
  183. data/docs/Tasker/Constants/ObservabilityEvents/Step.html +152 -0
  184. data/docs/Tasker/Constants/ObservabilityEvents/Task.html +142 -0
  185. data/docs/Tasker/Constants/ObservabilityEvents.html +126 -0
  186. data/docs/Tasker/Constants/RegistryEvents.html +285 -0
  187. data/docs/Tasker/Constants/StepEvents.html +177 -0
  188. data/docs/Tasker/Constants/TaskEvents.html +167 -0
  189. data/docs/Tasker/Constants/TaskExecution/ExecutionStatus.html +207 -0
  190. data/docs/Tasker/Constants/TaskExecution/HealthStatus.html +191 -0
  191. data/docs/Tasker/Constants/TaskExecution/RecommendedAction.html +207 -0
  192. data/docs/Tasker/Constants/TaskExecution.html +126 -0
  193. data/docs/Tasker/Constants/TaskFinalization/ErrorMessages.html +132 -0
  194. data/docs/Tasker/Constants/TaskFinalization/PendingReasons.html +207 -0
  195. data/docs/Tasker/Constants/TaskFinalization/ReenqueueReasons.html +239 -0
  196. data/docs/Tasker/Constants/TaskFinalization.html +126 -0
  197. data/docs/Tasker/Constants/TaskStatuses.html +223 -0
  198. data/docs/Tasker/Constants/TestEvents.html +163 -0
  199. data/docs/Tasker/Constants/WorkflowEvents.html +222 -0
  200. data/docs/Tasker/Constants/WorkflowStepStatuses.html +223 -0
  201. data/docs/Tasker/Constants.html +561 -0
  202. data/docs/Tasker/DependentSystem.html +137 -0
  203. data/docs/Tasker/DependentSystemObjectMap.html +250 -0
  204. data/docs/Tasker/DetectorRegistry.html +598 -0
  205. data/docs/Tasker/Diagram/Edge.html +1191 -0
  206. data/docs/Tasker/Diagram/Flowchart.html +1539 -0
  207. data/docs/Tasker/Diagram/Node.html +1165 -0
  208. data/docs/Tasker/Diagram.html +117 -0
  209. data/docs/Tasker/Engine.html +215 -0
  210. data/docs/Tasker/Error.html +139 -0
  211. data/docs/Tasker/Events/Bus.html +1226 -0
  212. data/docs/Tasker/Events/Catalog/CatalogPrinter.html +258 -0
  213. data/docs/Tasker/Events/Catalog/CustomEventRegistrar.html +276 -0
  214. data/docs/Tasker/Events/Catalog/ExamplePayloadGenerator.html +294 -0
  215. data/docs/Tasker/Events/Catalog.html +1291 -0
  216. data/docs/Tasker/Events/CustomRegistry.html +943 -0
  217. data/docs/Tasker/Events/DefinitionLoader.html +575 -0
  218. data/docs/Tasker/Events/EventPayloadBuilder/ErrorInfoExtractor.html +286 -0
  219. data/docs/Tasker/Events/EventPayloadBuilder/StepPayloadBuilder.html +312 -0
  220. data/docs/Tasker/Events/EventPayloadBuilder.html +664 -0
  221. data/docs/Tasker/Events/Publisher.html +365 -0
  222. data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer/ErrorTypeClassifier.html +1128 -0
  223. data/docs/Tasker/Events/Subscribers/BaseSubscriber/ErrorCategorizer.html +270 -0
  224. data/docs/Tasker/Events/Subscribers/BaseSubscriber/MetricTagsExtractor.html +266 -0
  225. data/docs/Tasker/Events/Subscribers/BaseSubscriber.html +2556 -0
  226. data/docs/Tasker/Events/Subscribers/MetricsSubscriber.html +723 -0
  227. data/docs/Tasker/Events/Subscribers/TelemetrySubscriber.html +2251 -0
  228. data/docs/Tasker/Events/Subscribers.html +117 -0
  229. data/docs/Tasker/Events/SubscriptionLoader.html +493 -0
  230. data/docs/Tasker/Events.html +294 -0
  231. data/docs/Tasker/EventsGenerator.html +459 -0
  232. data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics/AnalyticsMetrics.html +135 -0
  233. data/docs/Tasker/Functions/FunctionBasedAnalyticsMetrics.html +412 -0
  234. data/docs/Tasker/Functions/FunctionBasedDependencyLevels.html +598 -0
  235. data/docs/Tasker/Functions/FunctionBasedSlowestSteps/SlowestStep.html +135 -0
  236. data/docs/Tasker/Functions/FunctionBasedSlowestSteps.html +453 -0
  237. data/docs/Tasker/Functions/FunctionBasedSlowestTasks/SlowestTask.html +135 -0
  238. data/docs/Tasker/Functions/FunctionBasedSlowestTasks.html +453 -0
  239. data/docs/Tasker/Functions/FunctionBasedStepReadinessStatus.html +1457 -0
  240. data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts/HealthMetrics.html +135 -0
  241. data/docs/Tasker/Functions/FunctionBasedSystemHealthCounts.html +370 -0
  242. data/docs/Tasker/Functions/FunctionBasedTaskExecutionContext.html +1250 -0
  243. data/docs/Tasker/Functions/FunctionWrapper.html +479 -0
  244. data/docs/Tasker/Functions.html +117 -0
  245. data/docs/Tasker/Generators/AuthenticatorGenerator/UsageInstructionsFormatter.html +244 -0
  246. data/docs/Tasker/Generators/AuthenticatorGenerator.html +373 -0
  247. data/docs/Tasker/Generators/AuthorizationCoordinatorGenerator.html +430 -0
  248. data/docs/Tasker/Generators/SubscriberGenerator.html +377 -0
  249. data/docs/Tasker/Generators/TaskHandlerGenerator.html +263 -0
  250. data/docs/Tasker/Generators.html +117 -0
  251. data/docs/Tasker/GraphQLTypes/AnnotationType.html +132 -0
  252. data/docs/Tasker/GraphQLTypes/BaseArgument.html +124 -0
  253. data/docs/Tasker/GraphQLTypes/BaseConnection.html +124 -0
  254. data/docs/Tasker/GraphQLTypes/BaseEdge.html +130 -0
  255. data/docs/Tasker/GraphQLTypes/BaseEnum.html +124 -0
  256. data/docs/Tasker/GraphQLTypes/BaseField.html +124 -0
  257. data/docs/Tasker/GraphQLTypes/BaseInputObject.html +124 -0
  258. data/docs/Tasker/GraphQLTypes/BaseInterface.html +116 -0
  259. data/docs/Tasker/GraphQLTypes/BaseObject.html +128 -0
  260. data/docs/Tasker/GraphQLTypes/BaseScalar.html +124 -0
  261. data/docs/Tasker/GraphQLTypes/BaseUnion.html +124 -0
  262. data/docs/Tasker/GraphQLTypes/DependentSystemObjectMapType.html +132 -0
  263. data/docs/Tasker/GraphQLTypes/DependentSystemType.html +132 -0
  264. data/docs/Tasker/GraphQLTypes/MutationType.html +132 -0
  265. data/docs/Tasker/GraphQLTypes/NamedStepType.html +132 -0
  266. data/docs/Tasker/GraphQLTypes/NamedTaskType.html +132 -0
  267. data/docs/Tasker/GraphQLTypes/NamedTasksNamedStepType.html +132 -0
  268. data/docs/Tasker/GraphQLTypes/NodeType.html +118 -0
  269. data/docs/Tasker/GraphQLTypes/QueryType.html +139 -0
  270. data/docs/Tasker/GraphQLTypes/TaskAnnotationType.html +132 -0
  271. data/docs/Tasker/GraphQLTypes/TaskInterface.html +111 -0
  272. data/docs/Tasker/GraphQLTypes/TaskType.html +201 -0
  273. data/docs/Tasker/GraphQLTypes/WorkflowStepType.html +694 -0
  274. data/docs/Tasker/GraphQLTypes.html +130 -0
  275. data/docs/Tasker/GraphqlController.html +251 -0
  276. data/docs/Tasker/HandlerFactory.html +1518 -0
  277. data/docs/Tasker/HandlerSerializer.html +682 -0
  278. data/docs/Tasker/HandlersController.html +574 -0
  279. data/docs/Tasker/HashIdentityStrategy.html +278 -0
  280. data/docs/Tasker/Health/ReadinessChecker.html +712 -0
  281. data/docs/Tasker/Health/StatusChecker.html +653 -0
  282. data/docs/Tasker/Health.html +117 -0
  283. data/docs/Tasker/HealthController.html +523 -0
  284. data/docs/Tasker/IdentityStrategy.html +276 -0
  285. data/docs/Tasker/InvalidTaskHandlerConfig.html +135 -0
  286. data/docs/Tasker/LifecycleEvents/Events/Step.html +162 -0
  287. data/docs/Tasker/LifecycleEvents/Events/Task.html +162 -0
  288. data/docs/Tasker/LifecycleEvents/Events.html +204 -0
  289. data/docs/Tasker/LifecycleEvents/Publisher.html +132 -0
  290. data/docs/Tasker/LifecycleEvents.html +799 -0
  291. data/docs/Tasker/Logging/CorrelationIdGenerator.html +688 -0
  292. data/docs/Tasker/Logging.html +115 -0
  293. data/docs/Tasker/MetricsController.html +293 -0
  294. data/docs/Tasker/MetricsExportJob.html +414 -0
  295. data/docs/Tasker/Mutations/BaseMutation.html +128 -0
  296. data/docs/Tasker/Mutations/CancelStep.html +219 -0
  297. data/docs/Tasker/Mutations/CancelTask.html +221 -0
  298. data/docs/Tasker/Mutations/CreateTask.html +243 -0
  299. data/docs/Tasker/Mutations/UpdateStep.html +243 -0
  300. data/docs/Tasker/Mutations/UpdateTask.html +243 -0
  301. data/docs/Tasker/Mutations.html +117 -0
  302. data/docs/Tasker/NamedStep.html +216 -0
  303. data/docs/Tasker/NamedTask.html +910 -0
  304. data/docs/Tasker/NamedTasksNamedStep.html +435 -0
  305. data/docs/Tasker/Orchestration/BackoffCalculator.html +404 -0
  306. data/docs/Tasker/Orchestration/ConnectionBuilder/ConfigValidator.html +258 -0
  307. data/docs/Tasker/Orchestration/ConnectionBuilder.html +435 -0
  308. data/docs/Tasker/Orchestration/ConnectionPoolIntelligence.html +513 -0
  309. data/docs/Tasker/Orchestration/Coordinator.html +641 -0
  310. data/docs/Tasker/Orchestration/FutureStateAnalyzer.html +1045 -0
  311. data/docs/Tasker/Orchestration/Orchestrator.html +679 -0
  312. data/docs/Tasker/Orchestration/PluginIntegration.html +1127 -0
  313. data/docs/Tasker/Orchestration/ResponseProcessor.html +504 -0
  314. data/docs/Tasker/Orchestration/RetryHeaderParser.html +304 -0
  315. data/docs/Tasker/Orchestration/StepExecutor.html +995 -0
  316. data/docs/Tasker/Orchestration/StepSequenceFactory.html +644 -0
  317. data/docs/Tasker/Orchestration/TaskFinalizer/BlockageChecker.html +264 -0
  318. data/docs/Tasker/Orchestration/TaskFinalizer/ContextManager.html +254 -0
  319. data/docs/Tasker/Orchestration/TaskFinalizer/DelayCalculator.html +556 -0
  320. data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationDecisionMaker.html +348 -0
  321. data/docs/Tasker/Orchestration/TaskFinalizer/FinalizationProcessor.html +286 -0
  322. data/docs/Tasker/Orchestration/TaskFinalizer/ReasonDeterminer.html +432 -0
  323. data/docs/Tasker/Orchestration/TaskFinalizer/ReenqueueManager.html +296 -0
  324. data/docs/Tasker/Orchestration/TaskFinalizer/UnclearStateHandler.html +314 -0
  325. data/docs/Tasker/Orchestration/TaskFinalizer.html +1212 -0
  326. data/docs/Tasker/Orchestration/TaskInitializer.html +766 -0
  327. data/docs/Tasker/Orchestration/TaskReenqueuer.html +506 -0
  328. data/docs/Tasker/Orchestration/ViableStepDiscovery.html +442 -0
  329. data/docs/Tasker/Orchestration/WorkflowCoordinator.html +510 -0
  330. data/docs/Tasker/Orchestration.html +130 -0
  331. data/docs/Tasker/PageSort/PageSortParamsBuilder.html +296 -0
  332. data/docs/Tasker/PageSort.html +247 -0
  333. data/docs/Tasker/PermanentError.html +518 -0
  334. data/docs/Tasker/ProceduralError.html +147 -0
  335. data/docs/Tasker/Queries/AllAnnotationTypes.html +217 -0
  336. data/docs/Tasker/Queries/AllTasks.html +221 -0
  337. data/docs/Tasker/Queries/BaseQuery.html +128 -0
  338. data/docs/Tasker/Queries/Helpers.html +187 -0
  339. data/docs/Tasker/Queries/OneStep.html +225 -0
  340. data/docs/Tasker/Queries/OneTask.html +217 -0
  341. data/docs/Tasker/Queries/TasksByAnnotation.html +231 -0
  342. data/docs/Tasker/Queries/TasksByStatus.html +233 -0
  343. data/docs/Tasker/Queries.html +119 -0
  344. data/docs/Tasker/Railtie.html +124 -0
  345. data/docs/Tasker/Registry/BaseRegistry.html +1690 -0
  346. data/docs/Tasker/Registry/EventPublisher.html +667 -0
  347. data/docs/Tasker/Registry/InterfaceValidator.html +569 -0
  348. data/docs/Tasker/Registry/RegistrationError.html +132 -0
  349. data/docs/Tasker/Registry/RegistryError.html +139 -0
  350. data/docs/Tasker/Registry/StatisticsCollector.html +841 -0
  351. data/docs/Tasker/Registry/SubscriberRegistry.html +1504 -0
  352. data/docs/Tasker/Registry/ValidationError.html +132 -0
  353. data/docs/Tasker/Registry.html +119 -0
  354. data/docs/Tasker/RetryableError.html +515 -0
  355. data/docs/Tasker/StateMachine/Compatibility.html +282 -0
  356. data/docs/Tasker/StateMachine/InvalidStateTransition.html +135 -0
  357. data/docs/Tasker/StateMachine/StepStateMachine/StandardizedPayloadBuilder.html +260 -0
  358. data/docs/Tasker/StateMachine/StepStateMachine.html +2215 -0
  359. data/docs/Tasker/StateMachine/TaskStateMachine.html +734 -0
  360. data/docs/Tasker/StateMachine.html +602 -0
  361. data/docs/Tasker/StepDagRelationship.html +657 -0
  362. data/docs/Tasker/StepHandler/Api/Config.html +1091 -0
  363. data/docs/Tasker/StepHandler/Api.html +884 -0
  364. data/docs/Tasker/StepHandler/AutomaticEventPublishing.html +321 -0
  365. data/docs/Tasker/StepHandler/Base.html +970 -0
  366. data/docs/Tasker/StepHandler.html +119 -0
  367. data/docs/Tasker/StepReadinessStatus.html +836 -0
  368. data/docs/Tasker/Task.html +2575 -0
  369. data/docs/Tasker/TaskAnnotation.html +137 -0
  370. data/docs/Tasker/TaskAnnotationSerializer.html +124 -0
  371. data/docs/Tasker/TaskBuilder/StepNameValidator.html +264 -0
  372. data/docs/Tasker/TaskBuilder/StepTemplateDefiner.html +264 -0
  373. data/docs/Tasker/TaskBuilder.html +764 -0
  374. data/docs/Tasker/TaskDiagram/StepToStepEdgeBuilder.html +260 -0
  375. data/docs/Tasker/TaskDiagram/TaskToRootStepEdgeBuilder.html +290 -0
  376. data/docs/Tasker/TaskDiagram.html +548 -0
  377. data/docs/Tasker/TaskDiagramsController.html +240 -0
  378. data/docs/Tasker/TaskExecutionContext.html +469 -0
  379. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/ClassBasedEventRegistrar.html +238 -0
  380. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner/YamlEventRegistrar.html +254 -0
  381. data/docs/Tasker/TaskHandler/ClassMethods/StepTemplateDefiner.html +988 -0
  382. data/docs/Tasker/TaskHandler/ClassMethods.html +357 -0
  383. data/docs/Tasker/TaskHandler/InstanceMethods.html +1396 -0
  384. data/docs/Tasker/TaskHandler/StepGroup.html +1748 -0
  385. data/docs/Tasker/TaskHandler.html +271 -0
  386. data/docs/Tasker/TaskNamespace.html +312 -0
  387. data/docs/Tasker/TaskRunnerJob.html +406 -0
  388. data/docs/Tasker/TaskSerializer.html +474 -0
  389. data/docs/Tasker/TaskTransition.html +1517 -0
  390. data/docs/Tasker/TaskWorkflowSummary.html +988 -0
  391. data/docs/Tasker/TaskerRailsSchema/InvalidObjectTypeError.html +132 -0
  392. data/docs/Tasker/TaskerRailsSchema/TypeResolutionError.html +139 -0
  393. data/docs/Tasker/TaskerRailsSchema/UnknownInterfaceError.html +132 -0
  394. data/docs/Tasker/TaskerRailsSchema.html +384 -0
  395. data/docs/Tasker/TasksController.html +595 -0
  396. data/docs/Tasker/Telemetry/EventMapping.html +1307 -0
  397. data/docs/Tasker/Telemetry/EventRouter.html +2178 -0
  398. data/docs/Tasker/Telemetry/Events/ExportEvents.html +246 -0
  399. data/docs/Tasker/Telemetry/Events.html +115 -0
  400. data/docs/Tasker/Telemetry/ExportCoordinator/DistributedLockTimeoutError.html +135 -0
  401. data/docs/Tasker/Telemetry/ExportCoordinator.html +2137 -0
  402. data/docs/Tasker/Telemetry/IntelligentCacheManager.html +1083 -0
  403. data/docs/Tasker/Telemetry/LogBackend.html +1088 -0
  404. data/docs/Tasker/Telemetry/MetricTypes/Counter.html +1054 -0
  405. data/docs/Tasker/Telemetry/MetricTypes/Gauge.html +1270 -0
  406. data/docs/Tasker/Telemetry/MetricTypes/Histogram.html +1492 -0
  407. data/docs/Tasker/Telemetry/MetricTypes.html +153 -0
  408. data/docs/Tasker/Telemetry/MetricsBackend.html +2510 -0
  409. data/docs/Tasker/Telemetry/MetricsExportService.html +578 -0
  410. data/docs/Tasker/Telemetry/PluginRegistry.html +1774 -0
  411. data/docs/Tasker/Telemetry/Plugins/BaseExporter.html +1835 -0
  412. data/docs/Tasker/Telemetry/Plugins/CsvExporter.html +768 -0
  413. data/docs/Tasker/Telemetry/Plugins/JsonExporter.html +747 -0
  414. data/docs/Tasker/Telemetry/Plugins.html +117 -0
  415. data/docs/Tasker/Telemetry/PrometheusExporter.html +481 -0
  416. data/docs/Tasker/Telemetry/TraceBackend.html +891 -0
  417. data/docs/Tasker/Telemetry.html +130 -0
  418. data/docs/Tasker/Types/AuthConfig.html +886 -0
  419. data/docs/Tasker/Types/BackoffConfig.html +1063 -0
  420. data/docs/Tasker/Types/BaseConfig.html +227 -0
  421. data/docs/Tasker/Types/CacheConfig.html +1731 -0
  422. data/docs/Tasker/Types/DatabaseConfig.html +388 -0
  423. data/docs/Tasker/Types/DependencyGraph.html +526 -0
  424. data/docs/Tasker/Types/DependencyGraphConfig.html +753 -0
  425. data/docs/Tasker/Types/EngineConfig.html +1181 -0
  426. data/docs/Tasker/Types/ExecutionConfig.html +1963 -0
  427. data/docs/Tasker/Types/GraphEdge.html +517 -0
  428. data/docs/Tasker/Types/GraphMetadata.html +781 -0
  429. data/docs/Tasker/Types/GraphNode.html +694 -0
  430. data/docs/Tasker/Types/HealthConfig.html +784 -0
  431. data/docs/Tasker/Types/StepSequence.html +353 -0
  432. data/docs/Tasker/Types/StepTemplate.html +1193 -0
  433. data/docs/Tasker/Types/TaskRequest.html +1179 -0
  434. data/docs/Tasker/Types/TelemetryConfig.html +2746 -0
  435. data/docs/Tasker/Types.html +154 -0
  436. data/docs/Tasker/WorkflowStep/StepFinder.html +282 -0
  437. data/docs/Tasker/WorkflowStep.html +2724 -0
  438. data/docs/Tasker/WorkflowStepEdge.html +304 -0
  439. data/docs/Tasker/WorkflowStepSerializer.html +305 -0
  440. data/docs/Tasker/WorkflowStepTransition/TransitionDescriptionFormatter.html +282 -0
  441. data/docs/Tasker/WorkflowStepTransition.html +2201 -0
  442. data/docs/Tasker/WorkflowStepsController.html +462 -0
  443. data/docs/Tasker.html +452 -0
  444. data/docs/VISION.md +584 -0
  445. data/docs/WHY.md +21 -0
  446. data/docs/_index.html +2375 -0
  447. data/docs/class_list.html +54 -0
  448. data/docs/css/common.css +1 -0
  449. data/docs/css/full_list.css +58 -0
  450. data/docs/css/style.css +503 -0
  451. data/docs/events/migration_plan_outcomes.md +80 -0
  452. data/docs/file.README.html +541 -0
  453. data/docs/file_list.html +59 -0
  454. data/docs/frames.html +22 -0
  455. data/docs/index.html +541 -0
  456. data/docs/js/app.js +344 -0
  457. data/docs/js/full_list.js +242 -0
  458. data/docs/js/jquery.js +4 -0
  459. data/docs/method_list.html +9182 -0
  460. data/docs/top-level-namespace.html +110 -0
  461. data/lib/generators/tasker/authenticator_generator.rb +301 -0
  462. data/lib/generators/tasker/authorization_coordinator_generator.rb +139 -0
  463. data/lib/generators/tasker/events_generator.rb +91 -0
  464. data/lib/generators/tasker/subscriber_generator.rb +107 -0
  465. data/lib/generators/tasker/task_handler_generator.rb +138 -0
  466. data/lib/generators/tasker/templates/api_token_authenticator.rb.erb +113 -0
  467. data/lib/generators/tasker/templates/api_token_authenticator_spec.rb.erb +144 -0
  468. data/lib/generators/tasker/templates/authorization_coordinator.rb.erb +95 -0
  469. data/lib/generators/tasker/templates/authorization_coordinator_spec.rb.erb +142 -0
  470. data/lib/generators/tasker/templates/custom_authenticator.rb.erb +108 -0
  471. data/lib/generators/tasker/templates/custom_authenticator_spec.rb.erb +162 -0
  472. data/lib/generators/tasker/templates/custom_events.yml.erb +62 -0
  473. data/lib/generators/tasker/templates/custom_subscriber.rb.erb +72 -0
  474. data/lib/generators/tasker/templates/devise_authenticator.rb.erb +101 -0
  475. data/lib/generators/tasker/templates/devise_authenticator_spec.rb.erb +126 -0
  476. data/lib/generators/tasker/templates/initialize.rb.erb +202 -0
  477. data/lib/generators/tasker/templates/jwt_authenticator.rb.erb +144 -0
  478. data/lib/generators/tasker/templates/jwt_authenticator_spec.rb.erb +298 -0
  479. data/lib/generators/tasker/templates/metrics_subscriber.rb.erb +258 -0
  480. data/lib/generators/tasker/templates/metrics_subscriber_spec.rb.erb +308 -0
  481. data/lib/generators/tasker/templates/omniauth_authenticator.rb.erb +135 -0
  482. data/lib/generators/tasker/templates/omniauth_authenticator_spec.rb.erb +196 -0
  483. data/lib/generators/tasker/templates/opentelemetry_initializer.rb +52 -0
  484. data/lib/generators/tasker/templates/subscriber.rb.erb +64 -0
  485. data/lib/generators/tasker/templates/subscriber_spec.rb.erb +80 -0
  486. data/lib/generators/tasker/templates/task_config.yaml.erb +117 -0
  487. data/lib/generators/tasker/templates/task_handler.rb.erb +59 -0
  488. data/lib/generators/tasker/templates/task_handler_spec.rb.erb +159 -0
  489. data/lib/tasker/analysis/runtime_graph_analyzer.rb +1168 -0
  490. data/lib/tasker/analysis/template_graph_analyzer.rb +328 -0
  491. data/lib/tasker/authentication/coordinator.rb +78 -0
  492. data/lib/tasker/authentication/errors.rb +9 -0
  493. data/lib/tasker/authentication/interface.rb +36 -0
  494. data/lib/tasker/authentication/none_authenticator.rb +26 -0
  495. data/lib/tasker/authorization/base_coordinator.rb +112 -0
  496. data/lib/tasker/authorization/errors.rb +26 -0
  497. data/lib/tasker/authorization/resource_constants.rb +74 -0
  498. data/lib/tasker/authorization/resource_registry.rb +143 -0
  499. data/lib/tasker/authorization.rb +75 -0
  500. data/lib/tasker/cache_capabilities.rb +131 -0
  501. data/lib/tasker/cache_strategy.rb +469 -0
  502. data/lib/tasker/concerns/authenticatable.rb +41 -0
  503. data/lib/tasker/concerns/authorizable.rb +204 -0
  504. data/lib/tasker/concerns/controller_authorizable.rb +124 -0
  505. data/lib/tasker/concerns/event_publisher.rb +716 -0
  506. data/lib/tasker/concerns/idempotent_state_transitions.rb +128 -0
  507. data/lib/tasker/concerns/state_machine_base.rb +218 -0
  508. data/lib/tasker/concerns/structured_logging.rb +387 -0
  509. data/lib/tasker/configuration.rb +325 -0
  510. data/lib/tasker/constants/event_definitions.rb +147 -0
  511. data/lib/tasker/constants/registry_events.rb +54 -0
  512. data/lib/tasker/constants.rb +417 -0
  513. data/lib/tasker/engine.rb +90 -0
  514. data/lib/tasker/errors.rb +90 -0
  515. data/lib/tasker/events/catalog.rb +432 -0
  516. data/lib/tasker/events/custom_registry.rb +175 -0
  517. data/lib/tasker/events/definition_loader.rb +199 -0
  518. data/lib/tasker/events/event_payload_builder.rb +461 -0
  519. data/lib/tasker/events/publisher.rb +149 -0
  520. data/lib/tasker/events/subscribers/base_subscriber.rb +601 -0
  521. data/lib/tasker/events/subscribers/metrics_subscriber.rb +120 -0
  522. data/lib/tasker/events/subscribers/telemetry_subscriber.rb +462 -0
  523. data/lib/tasker/events/subscription_loader.rb +161 -0
  524. data/lib/tasker/events.rb +37 -0
  525. data/lib/tasker/functions/function_based_analytics_metrics.rb +103 -0
  526. data/lib/tasker/functions/function_based_dependency_levels.rb +54 -0
  527. data/lib/tasker/functions/function_based_slowest_steps.rb +84 -0
  528. data/lib/tasker/functions/function_based_slowest_tasks.rb +84 -0
  529. data/lib/tasker/functions/function_based_step_readiness_status.rb +183 -0
  530. data/lib/tasker/functions/function_based_system_health_counts.rb +94 -0
  531. data/lib/tasker/functions/function_based_task_execution_context.rb +148 -0
  532. data/lib/tasker/functions/function_wrapper.rb +42 -0
  533. data/lib/tasker/functions.rb +12 -0
  534. data/lib/tasker/handler_factory.rb +322 -0
  535. data/lib/tasker/health/readiness_checker.rb +186 -0
  536. data/lib/tasker/health/status_checker.rb +203 -0
  537. data/lib/tasker/identity_strategy.rb +38 -0
  538. data/lib/tasker/logging/correlation_id_generator.rb +120 -0
  539. data/lib/tasker/orchestration/backoff_calculator.rb +184 -0
  540. data/lib/tasker/orchestration/connection_builder.rb +122 -0
  541. data/lib/tasker/orchestration/connection_pool_intelligence.rb +177 -0
  542. data/lib/tasker/orchestration/coordinator.rb +119 -0
  543. data/lib/tasker/orchestration/future_state_analyzer.rb +137 -0
  544. data/lib/tasker/orchestration/plugin_integration.rb +124 -0
  545. data/lib/tasker/orchestration/response_processor.rb +168 -0
  546. data/lib/tasker/orchestration/retry_header_parser.rb +78 -0
  547. data/lib/tasker/orchestration/step_executor.rb +941 -0
  548. data/lib/tasker/orchestration/step_sequence_factory.rb +67 -0
  549. data/lib/tasker/orchestration/task_finalizer.rb +564 -0
  550. data/lib/tasker/orchestration/task_initializer.rb +140 -0
  551. data/lib/tasker/orchestration/task_reenqueuer.rb +71 -0
  552. data/lib/tasker/orchestration/viable_step_discovery.rb +65 -0
  553. data/lib/tasker/orchestration/workflow_coordinator.rb +294 -0
  554. data/lib/tasker/orchestration.rb +45 -0
  555. data/lib/tasker/railtie.rb +9 -0
  556. data/lib/tasker/registry/base_registry.rb +177 -0
  557. data/lib/tasker/registry/event_publisher.rb +91 -0
  558. data/lib/tasker/registry/interface_validator.rb +140 -0
  559. data/lib/tasker/registry/statistics_collector.rb +381 -0
  560. data/lib/tasker/registry/subscriber_registry.rb +285 -0
  561. data/lib/tasker/registry.rb +22 -0
  562. data/lib/tasker/state_machine/step_state_machine.rb +508 -0
  563. data/lib/tasker/state_machine/task_state_machine.rb +192 -0
  564. data/lib/tasker/state_machine.rb +83 -0
  565. data/lib/tasker/step_handler/api.rb +410 -0
  566. data/lib/tasker/step_handler/base.rb +206 -0
  567. data/lib/tasker/task_builder.rb +432 -0
  568. data/lib/tasker/task_handler/class_methods.rb +324 -0
  569. data/lib/tasker/task_handler/instance_methods.rb +293 -0
  570. data/lib/tasker/task_handler/step_group.rb +182 -0
  571. data/lib/tasker/task_handler.rb +43 -0
  572. data/lib/tasker/telemetry/event_mapping.rb +126 -0
  573. data/lib/tasker/telemetry/event_router.rb +318 -0
  574. data/lib/tasker/telemetry/events/export_events.rb +38 -0
  575. data/lib/tasker/telemetry/export_coordinator.rb +497 -0
  576. data/lib/tasker/telemetry/intelligent_cache_manager.rb +508 -0
  577. data/lib/tasker/telemetry/log_backend.rb +224 -0
  578. data/lib/tasker/telemetry/metric_types.rb +368 -0
  579. data/lib/tasker/telemetry/metrics_backend.rb +1227 -0
  580. data/lib/tasker/telemetry/metrics_export_service.rb +392 -0
  581. data/lib/tasker/telemetry/plugin_registry.rb +333 -0
  582. data/lib/tasker/telemetry/plugins/base_exporter.rb +246 -0
  583. data/lib/tasker/telemetry/plugins/csv_exporter.rb +198 -0
  584. data/lib/tasker/telemetry/plugins/json_exporter.rb +141 -0
  585. data/lib/tasker/telemetry/prometheus_exporter.rb +249 -0
  586. data/lib/tasker/telemetry/trace_backend.rb +186 -0
  587. data/lib/tasker/telemetry.rb +59 -0
  588. data/lib/tasker/types/auth_config.rb +81 -0
  589. data/lib/tasker/types/backoff_config.rb +142 -0
  590. data/lib/tasker/types/cache_config.rb +257 -0
  591. data/lib/tasker/types/database_config.rb +39 -0
  592. data/lib/tasker/types/dependency_graph.rb +225 -0
  593. data/lib/tasker/types/dependency_graph_config.rb +149 -0
  594. data/lib/tasker/types/engine_config.rb +131 -0
  595. data/lib/tasker/types/execution_config.rb +289 -0
  596. data/lib/tasker/types/health_config.rb +84 -0
  597. data/lib/tasker/types/step_sequence.rb +24 -0
  598. data/lib/tasker/types/step_template.rb +63 -0
  599. data/lib/tasker/types/task_request.rb +60 -0
  600. data/lib/tasker/types/telemetry_config.rb +273 -0
  601. data/lib/tasker/types.rb +64 -0
  602. data/lib/tasker/version.rb +7 -0
  603. data/lib/tasker.rb +82 -0
  604. data/lib/tasks/tasker_tasks.rake +302 -0
  605. 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.**