diff --git a/rust/README.md b/rust/README.md index 18c297ab0..e10197825 100644 --- a/rust/README.md +++ b/rust/README.md @@ -355,6 +355,19 @@ completing worker's contract once, with its first accepted workflow task completion. Until then its updates return HTTP 409 `contract_pending`. Stop the native nodes, back up the database and run `schema-upgrade` before starting this binary on an earlier schema. +MySQL schema 13 (Server #1016) stores every JSON document column as exact +`utf8mb4_bin` text with an enforced `json_valid` check, as MariaDB already +does, so doubles keep every digit. MySQL's `json` type parsed doubles itself +and could store a value 1 ULP off. Only MySQL changes: MariaDB and PostgreSQL stay +at schema 12 and SQLite at 11. The upgrade rebuilds the 36 tables that hold +JSON columns, one atomic `ALTER TABLE` each, and an interrupted upgrade resumes +table by table. On MySQL: stop every native node, back up the database, run +`schema-upgrade` once, then start the new nodes. The service is down for the +whole upgrade, and the time grows with the data: measured on MySQL 8.0.46 with +2 CPUs and the default 128 MiB buffer pool, 1,000,000 history rows (2.65 GB) +took 115 s, about 2 minutes per million history rows; plan for the other JSON +tables too. Documents already stored keep MySQL's rendering, including any +rounded doubles. Rolling back means restoring the backup. Implemented paths cover health/schema readiness, limited capability discovery, workflow start/describe/paginated history, worker registration/heartbeat/removal, diff --git a/rust/migrations/mariadb/0012_checksum.txt b/rust/migrations/mariadb/0012_checksum.txt new file mode 100644 index 000000000..6d3198d0d --- /dev/null +++ b/rust/migrations/mariadb/0012_checksum.txt @@ -0,0 +1 @@ +477621f4b2083ef963b6db6ed530e7eb5d72704f32f250f0640bb99a770f420f78f13277fcd2cc5cb23a5928965cd9e2 diff --git a/rust/migrations/mysql/0012_checksum.txt b/rust/migrations/mysql/0012_checksum.txt new file mode 100644 index 000000000..f027ac31a --- /dev/null +++ b/rust/migrations/mysql/0012_checksum.txt @@ -0,0 +1 @@ +f5430aad3bb99703601105763a97089326f4e842792eb98b15518d069aedf4bb8582add033aeae66e68c14f49bcc6a64 diff --git a/rust/migrations/mysql/0013_catalog.json b/rust/migrations/mysql/0013_catalog.json new file mode 100644 index 000000000..3902fe3d9 --- /dev/null +++ b/rust/migrations/mysql/0013_catalog.json @@ -0,0 +1,246 @@ +[ + [ + "_sqlx_migrations", + "CREATE TABLE `_sqlx_migrations` (\n `version` bigint NOT NULL,\n `description` text COLLATE utf8mb4_unicode_ci NOT NULL,\n `installed_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n `success` tinyint(1) NOT NULL,\n `checksum` blob NOT NULL,\n `execution_time` bigint NOT NULL,\n PRIMARY KEY (`version`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "activity_attempts", + "CREATE TABLE `activity_attempts` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `activity_execution_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_task_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `worker_attempt_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `attempt_number` int unsigned NOT NULL,\n `status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `lease_owner` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `started_at` timestamp(6) NOT NULL,\n `last_heartbeat_at` timestamp(6) NULL DEFAULT NULL,\n `lease_expires_at` timestamp(6) NULL DEFAULT NULL,\n `closed_at` timestamp(6) NULL DEFAULT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `activity_attempts_activity_execution_id_attempt_number_unique` (`activity_execution_id`,`attempt_number`),\n KEY `activity_attempts_workflow_run_id_index` (`workflow_run_id`),\n KEY `activity_attempts_activity_execution_id_index` (`activity_execution_id`),\n KEY `activity_attempts_workflow_task_id_index` (`workflow_task_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "activity_executions", + "CREATE TABLE `activity_executions` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `sequence` int unsigned NOT NULL,\n `activity_class` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `activity_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `payload_codec` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `arguments` longtext COLLATE utf8mb4_unicode_ci,\n `result` longtext COLLATE utf8mb4_unicode_ci,\n `exception` longtext COLLATE utf8mb4_unicode_ci,\n `connection` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `queue` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `attempt_count` int unsigned NOT NULL DEFAULT '1',\n `current_attempt_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `retry_policy` longtext COLLATE utf8mb4_bin,\n `parallel_group_path` longtext COLLATE utf8mb4_bin,\n `activity_options` longtext COLLATE utf8mb4_bin,\n `schedule_deadline_at` timestamp(6) NULL DEFAULT NULL,\n `close_deadline_at` timestamp(6) NULL DEFAULT NULL,\n `schedule_to_close_deadline_at` timestamp(6) NULL DEFAULT NULL,\n `heartbeat_deadline_at` timestamp(6) NULL DEFAULT NULL,\n `started_at` timestamp(6) NULL DEFAULT NULL,\n `closed_at` timestamp(6) NULL DEFAULT NULL,\n `last_heartbeat_at` timestamp(6) NULL DEFAULT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `activity_executions_workflow_run_id_sequence_unique` (`workflow_run_id`,`sequence`),\n KEY `activity_executions_workflow_run_id_index` (`workflow_run_id`),\n KEY `activity_exec_prom_metrics_series_idx` (`workflow_run_id`,`queue`,`activity_type`),\n CONSTRAINT `dw_json_text_5f011f3fc9ed1cbf3798` CHECK (json_valid(`activity_options`)),\n CONSTRAINT `dw_json_text_b0ad277df9011ce8dcd8` CHECK (json_valid(`parallel_group_path`)),\n CONSTRAINT `dw_json_text_da40cddf645f92a0f5d3` CHECK (json_valid(`retry_policy`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "dw_external_payload_deletions", + "CREATE TABLE `dw_external_payload_deletions` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `storage_uri` text COLLATE utf8mb4_unicode_ci NOT NULL,\n `storage_uri_sha256` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `storage_policy` longtext COLLATE utf8mb4_unicode_ci NOT NULL,\n `attempts` bigint NOT NULL DEFAULT '0',\n `failure_code` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `created_at` datetime(6) NOT NULL,\n `updated_at` datetime(6) NOT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `dw_external_payload_deletions_uri` (`namespace`,`storage_uri_sha256`),\n KEY `dw_external_payload_deletions_due` (`namespace`,`updated_at`,`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "dw_poll_receipts", + "CREATE TABLE `dw_poll_receipts` (\n `namespace` varchar(255) COLLATE utf8mb4_0900_bin NOT NULL,\n `worker_id` varchar(255) COLLATE utf8mb4_0900_bin NOT NULL,\n `kind` varchar(8) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,\n `request_id` varchar(255) COLLATE utf8mb4_0900_bin NOT NULL,\n `task_id` varchar(26) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n `attempt` bigint NOT NULL,\n `response` longtext COLLATE utf8mb4_bin NOT NULL,\n `expires_at` timestamp(6) NOT NULL,\n PRIMARY KEY (`namespace`,`worker_id`,`kind`,`request_id`),\n KEY `dw_poll_receipts_task` (`task_id`),\n KEY `dw_poll_receipts_expiration` (`expires_at`),\n CONSTRAINT `dw_poll_receipts_task` FOREIGN KEY (`task_id`) REFERENCES `workflow_tasks` (`id`) ON DELETE CASCADE,\n CONSTRAINT `dw_json_text_b5a74d059c497aac89e1` CHECK (json_valid(`response`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin" + ], + [ + "dw_query_cache", + "CREATE TABLE `dw_query_cache` (\n `key` varchar(64) COLLATE utf8mb4_bin NOT NULL,\n `value` longtext COLLATE utf8mb4_bin NOT NULL,\n `expiration` bigint NOT NULL,\n PRIMARY KEY (`key`),\n KEY `dw_query_cache_expiration` (`expiration`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin" + ], + [ + "dw_run_command_contracts", + "CREATE TABLE `dw_run_command_contracts` (\n `workflow_run_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `contract` longtext COLLATE utf8mb4_unicode_ci NOT NULL,\n `worker_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `workflow_task_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `bound_at` datetime(6) NOT NULL,\n PRIMARY KEY (`workflow_run_id`),\n KEY `dw_run_command_contracts_namespace` (`namespace`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "dw_server_schema", + "CREATE TABLE `dw_server_schema` (\n `engine` varchar(64) COLLATE utf8mb4_bin NOT NULL,\n `version` bigint NOT NULL,\n `bootstrap_checksum` varbinary(48) NOT NULL,\n PRIMARY KEY (`engine`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin" + ], + [ + "dw_service_admission_counters", + "CREATE TABLE `dw_service_admission_counters` (\n `cache_key` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `amount` bigint NOT NULL,\n `expires_at` datetime(6) NOT NULL,\n PRIMARY KEY (`cache_key`),\n KEY `dw_service_admission_counters_expiry` (`expires_at`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "dw_service_call_deadlines", + "CREATE TABLE `dw_service_call_deadlines` (\n `service_call_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `call_deadline_at` datetime(6) NOT NULL,\n PRIMARY KEY (`service_call_id`),\n KEY `dw_service_call_deadlines_due` (`call_deadline_at`,`service_call_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "dw_stream_reclaims", + "CREATE TABLE `dw_stream_reclaims` (\n `stream_id` bigint unsigned NOT NULL,\n `namespace` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `reclaim_at` datetime(6) NOT NULL,\n PRIMARY KEY (`stream_id`),\n KEY `dw_stream_reclaims_due` (`namespace`,`reclaim_at`,`stream_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "dw_task_completions", + "CREATE TABLE `dw_task_completions` (\n `task_id` varchar(26) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n `receipt` longtext COLLATE utf8mb4_bin NOT NULL,\n PRIMARY KEY (`task_id`),\n CONSTRAINT `dw_task_completions_task` FOREIGN KEY (`task_id`) REFERENCES `workflow_tasks` (`id`) ON DELETE CASCADE,\n CONSTRAINT `dw_json_text_49816cb4519d3d981518` CHECK (json_valid(`receipt`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "dw_worker_registration_incarnations", + "CREATE TABLE `dw_worker_registration_incarnations` (\n `token` char(32) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,\n `namespace` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `worker_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `status` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,\n `recovered_workflow_task_count` bigint DEFAULT NULL,\n `finished_at` datetime(6) DEFAULT NULL,\n `created_at` datetime(6) NOT NULL,\n `updated_at` datetime(6) NOT NULL,\n PRIMARY KEY (`token`),\n KEY `dw_worker_incarnation_authority` (`namespace`,`worker_id`,`status`),\n KEY `dw_worker_incarnation_retention` (`status`,`finished_at`),\n CONSTRAINT `dw_worker_incarnation_state` CHECK ((((`status` = _utf8mb4'active') and (`finished_at` is null) and (`recovered_workflow_task_count` is null)) or ((`status` = _utf8mb4'superseded') and (`finished_at` is not null) and (`recovered_workflow_task_count` is null)) or ((`status` = _utf8mb4'deregistered') and (`finished_at` is not null) and (`recovered_workflow_task_count` is not null) and (`recovered_workflow_task_count` >= 0))))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "dw_worker_session_holders", + "CREATE TABLE `dw_worker_session_holders` (\n `namespace` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `session_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `incarnation` char(64) COLLATE ascii_bin NOT NULL,\n `updated_at` datetime(6) NOT NULL,\n PRIMARY KEY (`namespace`,`session_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "failed_jobs", + "CREATE TABLE `failed_jobs` (\n `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n `uuid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `connection` text COLLATE utf8mb4_unicode_ci NOT NULL,\n `queue` text COLLATE utf8mb4_unicode_ci NOT NULL,\n `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,\n `exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,\n `failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`),\n UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "jobs", + "CREATE TABLE `jobs` (\n `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n `queue` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,\n `attempts` tinyint unsigned NOT NULL,\n `reserved_at` int unsigned DEFAULT NULL,\n `available_at` int unsigned NOT NULL,\n `created_at` int unsigned NOT NULL,\n PRIMARY KEY (`id`),\n KEY `jobs_queue_index` (`queue`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "migrations", + "CREATE TABLE `migrations` (\n `id` int unsigned NOT NULL AUTO_INCREMENT,\n `migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `batch` int NOT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "runtime_credentials", + "CREATE TABLE `runtime_credentials` (\n `id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `subject` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `roles` longtext COLLATE utf8mb4_bin NOT NULL,\n `tenant` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `claims` longtext COLLATE utf8mb4_bin,\n `token_prefix` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL,\n `token_hash` char(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `expires_at` timestamp NULL DEFAULT NULL,\n `revoked_at` timestamp NULL DEFAULT NULL,\n `rotated_at` timestamp NULL DEFAULT NULL,\n `created_at` timestamp NULL DEFAULT NULL,\n `updated_at` timestamp NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `runtime_credentials_token_hash_unique` (`token_hash`),\n KEY `runtime_credentials_tenant_index` (`tenant`),\n KEY `runtime_credentials_expires_at_index` (`expires_at`),\n KEY `runtime_credentials_revoked_at_index` (`revoked_at`),\n CONSTRAINT `dw_json_text_075fecde0a757510fdf7` CHECK (json_valid(`roles`)),\n CONSTRAINT `dw_json_text_5f7fdbad00226a90f331` CHECK (json_valid(`claims`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "runtime_external_payload_backup_hold", + "CREATE TABLE `runtime_external_payload_backup_hold` (\n `id` tinyint unsigned NOT NULL,\n `owner` char(36) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `acquired_at` timestamp NULL DEFAULT NULL,\n `expires_at` timestamp NULL DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "runtime_external_payload_cleanup_stats", + "CREATE TABLE `runtime_external_payload_cleanup_stats` (\n `namespace` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `passes_total` bigint unsigned NOT NULL DEFAULT '0',\n `deleted_references_total` bigint unsigned NOT NULL DEFAULT '0',\n `deleted_backing_objects_total` bigint unsigned NOT NULL DEFAULT '0',\n `shared_objects_preserved_total` bigint unsigned NOT NULL DEFAULT '0',\n `blocked_outcomes_total` bigint unsigned NOT NULL DEFAULT '0',\n `storage_driver_failures_total` bigint unsigned NOT NULL DEFAULT '0',\n `last_processed` int unsigned NOT NULL DEFAULT '0',\n `last_deleted_references` int unsigned NOT NULL DEFAULT '0',\n `last_deleted_backing_objects` int unsigned NOT NULL DEFAULT '0',\n `last_shared_objects_preserved` int unsigned NOT NULL DEFAULT '0',\n `last_blocked_outcomes` int unsigned NOT NULL DEFAULT '0',\n `last_storage_driver_failures` int unsigned NOT NULL DEFAULT '0',\n `last_pass_status` varchar(16) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `last_completed_at` timestamp NULL DEFAULT NULL,\n `last_storage_failure_at` timestamp NULL DEFAULT NULL,\n `created_at` timestamp NULL DEFAULT NULL,\n `updated_at` timestamp NULL DEFAULT NULL,\n PRIMARY KEY (`namespace`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "runtime_external_payload_object_locks", + "CREATE TABLE `runtime_external_payload_object_locks` (\n `bucket` smallint unsigned NOT NULL,\n PRIMARY KEY (`bucket`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "runtime_external_payloads", + "CREATE TABLE `runtime_external_payloads` (\n `id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `storage_uri` text COLLATE utf8mb4_unicode_ci NOT NULL,\n `storage_uri_sha256` char(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `codec` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `sha256` char(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `size_bytes` bigint unsigned NOT NULL,\n `upload_status` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'ready',\n `retained_at` timestamp NULL DEFAULT NULL,\n `expires_at` timestamp NULL DEFAULT NULL,\n `last_fetched_at` timestamp NULL DEFAULT NULL,\n `created_at` timestamp NULL DEFAULT NULL,\n `updated_at` timestamp NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `runtime_external_payloads_namespace_uri_unique` (`namespace`,`storage_uri_sha256`),\n KEY `runtime_external_payloads_namespace_index` (`namespace`),\n KEY `runtime_external_payloads_expires_at_index` (`expires_at`),\n KEY `runtime_external_payloads_reclamation_idx` (`retained_at`,`expires_at`,`upload_status`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "runtime_payload_completion_budgets", + "CREATE TABLE `runtime_payload_completion_budgets` (\n `id` char(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `context` longtext COLLATE utf8mb4_bin NOT NULL,\n `slots` longtext COLLATE utf8mb4_bin NOT NULL,\n `objects` longtext COLLATE utf8mb4_bin NOT NULL,\n `expires_at` timestamp NOT NULL,\n `created_at` timestamp NULL DEFAULT NULL,\n `updated_at` timestamp NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `runtime_payload_completion_budgets_namespace_index` (`namespace`),\n KEY `runtime_payload_completion_budgets_expires_at_index` (`expires_at`),\n CONSTRAINT `dw_json_text_4077efaae491546bebc7` CHECK (json_valid(`slots`)),\n CONSTRAINT `dw_json_text_c185060f2dbee09c5a6a` CHECK (json_valid(`objects`)),\n CONSTRAINT `dw_json_text_d1bf52994d698d7d04ed` CHECK (json_valid(`context`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "search_attribute_definitions", + "CREATE TABLE `search_attribute_definitions` (\n `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n `namespace` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `type` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,\n `created_at` timestamp NULL DEFAULT NULL,\n `updated_at` timestamp NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `search_attribute_definitions_namespace_name_unique` (`namespace`,`name`),\n KEY `search_attribute_definitions_namespace_index` (`namespace`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_child_calls", + "CREATE TABLE `workflow_child_calls` (\n `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n `parent_workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `parent_workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `sequence` int unsigned NOT NULL,\n `child_workflow_type` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `child_workflow_class` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `requested_child_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `resolved_child_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `resolved_child_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `parent_close_policy` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'abandon',\n `connection` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `queue` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `compatibility` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `retry_policy` longtext COLLATE utf8mb4_bin,\n `timeout_policy` longtext COLLATE utf8mb4_bin,\n `cancellation_propagation` tinyint(1) NOT NULL DEFAULT '0',\n `status` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'scheduled',\n `result_payload_reference` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `failure_reference` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `closed_reason` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `scheduled_at` timestamp(6) NOT NULL,\n `started_at` timestamp(6) NULL DEFAULT NULL,\n `closed_at` timestamp(6) NULL DEFAULT NULL,\n `arguments` longtext COLLATE utf8mb4_bin,\n `metadata` longtext COLLATE utf8mb4_bin,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `child_calls_parent_seq` (`parent_workflow_run_id`,`sequence`),\n KEY `child_calls_parent_status` (`parent_workflow_run_id`,`status`),\n KEY `child_calls_child_parent` (`resolved_child_instance_id`,`parent_workflow_run_id`),\n KEY `workflow_child_calls_parent_workflow_run_id_index` (`parent_workflow_run_id`),\n KEY `workflow_child_calls_parent_workflow_instance_id_index` (`parent_workflow_instance_id`),\n KEY `workflow_child_calls_sequence_index` (`sequence`),\n KEY `workflow_child_calls_resolved_child_instance_id_index` (`resolved_child_instance_id`),\n KEY `workflow_child_calls_resolved_child_run_id_index` (`resolved_child_run_id`),\n KEY `workflow_child_calls_status_index` (`status`),\n CONSTRAINT `workflow_child_calls_parent_workflow_run_id_foreign` FOREIGN KEY (`parent_workflow_run_id`) REFERENCES `workflow_runs` (`id`) ON DELETE CASCADE,\n CONSTRAINT `dw_json_text_016208f3b76c51671353` CHECK (json_valid(`metadata`)),\n CONSTRAINT `dw_json_text_40e45e4f94944c511ab5` CHECK (json_valid(`timeout_policy`)),\n CONSTRAINT `dw_json_text_55a3cac14b0a3feca840` CHECK (json_valid(`retry_policy`)),\n CONSTRAINT `dw_json_text_9c078ba0f6c256bb7d9c` CHECK (json_valid(`arguments`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_child_projection_repairs", + "CREATE TABLE `workflow_child_projection_repairs` (\n `workflow_history_event_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_task_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `history_sequence` int unsigned NOT NULL,\n `failure_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `failed_child_counted_at` timestamp(6) NULL DEFAULT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`workflow_history_event_id`),\n KEY `workflow_child_projection_repairs_drain_idx` (`workflow_run_id`,`workflow_task_id`,`history_sequence`),\n KEY `workflow_child_projection_repairs_failure_idx` (`workflow_run_id`,`failure_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_commands", + "CREATE TABLE `workflow_commands` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `requested_workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `resolved_workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `command_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `target_scope` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'instance',\n `source` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'php',\n `context` longtext COLLATE utf8mb4_bin,\n `request_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `outcome` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `workflow_class` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `workflow_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `payload_codec` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `payload` longtext COLLATE utf8mb4_unicode_ci,\n `rejection_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `command_sequence` int unsigned DEFAULT NULL,\n `message_sequence` int unsigned DEFAULT NULL,\n `accepted_at` timestamp(6) NULL DEFAULT NULL,\n `applied_at` timestamp(6) NULL DEFAULT NULL,\n `rejected_at` timestamp(6) NULL DEFAULT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_commands_request_lookup` (`workflow_instance_id`,`command_type`,`request_id`),\n KEY `workflow_commands_workflow_instance_id_index` (`workflow_instance_id`),\n KEY `workflow_commands_workflow_run_id_index` (`workflow_run_id`),\n KEY `workflow_commands_requested_workflow_run_id_index` (`requested_workflow_run_id`),\n KEY `workflow_commands_resolved_workflow_run_id_index` (`resolved_workflow_run_id`),\n KEY `workflow_commands_command_type_index` (`command_type`),\n KEY `workflow_commands_source_index` (`source`),\n KEY `workflow_commands_status_index` (`status`),\n KEY `workflow_commands_command_sequence_index` (`command_sequence`),\n CONSTRAINT `dw_json_text_1365410084c0cd12ed37` CHECK (json_valid(`context`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_durable_stream_items", + "CREATE TABLE `workflow_durable_stream_items` (\n `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n `stream_id` bigint unsigned NOT NULL,\n `namespace` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `stream_name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `offset` bigint NOT NULL,\n `idempotency_key` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `origin` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'workflow_command',\n `origin_reference` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `payload` longtext COLLATE utf8mb4_bin,\n `payload_reference` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `payload_codec` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `item_type` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `content_type` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `emitted_at` timestamp(6) NOT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `wf_durable_stream_items_stream_offset_unique` (`stream_id`,`offset`),\n UNIQUE KEY `wf_durable_stream_items_idempotency_unique` (`stream_id`,`idempotency_key`),\n KEY `wf_durable_stream_items_run_name_offset_idx` (`workflow_run_id`,`stream_name`,`offset`),\n KEY `workflow_durable_stream_items_namespace_index` (`namespace`),\n KEY `workflow_durable_stream_items_workflow_run_id_index` (`workflow_run_id`),\n CONSTRAINT `workflow_durable_stream_items_stream_id_foreign` FOREIGN KEY (`stream_id`) REFERENCES `workflow_durable_streams` (`id`) ON DELETE CASCADE,\n CONSTRAINT `dw_json_text_9d624f24054e91d9250f` CHECK (json_valid(`payload`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_durable_streams", + "CREATE TABLE `workflow_durable_streams` (\n `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n `namespace` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `stream_name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `status` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'open',\n `last_offset` bigint NOT NULL DEFAULT '-1',\n `total_items` bigint unsigned NOT NULL DEFAULT '0',\n `retention_seconds` int unsigned DEFAULT NULL,\n `opened_at` timestamp(6) NULL DEFAULT NULL,\n `last_appended_at` timestamp(6) NULL DEFAULT NULL,\n `closed_at` timestamp(6) NULL DEFAULT NULL,\n `error_reason` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `pending_items` bigint unsigned NOT NULL DEFAULT '0',\n `metadata` longtext COLLATE utf8mb4_bin,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `wf_durable_streams_run_name_unique` (`workflow_run_id`,`stream_name`),\n KEY `wf_durable_streams_ns_instance_name_idx` (`namespace`,`workflow_instance_id`,`stream_name`),\n KEY `wf_durable_streams_ns_status_idx` (`namespace`,`status`),\n KEY `workflow_durable_streams_namespace_index` (`namespace`),\n KEY `workflow_durable_streams_workflow_instance_id_index` (`workflow_instance_id`),\n KEY `workflow_durable_streams_workflow_run_id_index` (`workflow_run_id`),\n KEY `workflow_durable_streams_status_index` (`status`),\n CONSTRAINT `dw_json_text_da0f067f234347705b1c` CHECK (json_valid(`metadata`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_failures", + "CREATE TABLE `workflow_failures` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `source_kind` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `source_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `propagation_kind` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `failure_category` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `non_retryable` tinyint(1) NOT NULL DEFAULT '0',\n `handled` tinyint(1) NOT NULL DEFAULT '0',\n `exception_class` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `message` text COLLATE utf8mb4_unicode_ci NOT NULL,\n `file` text COLLATE utf8mb4_unicode_ci NOT NULL,\n `line` int unsigned DEFAULT NULL,\n `trace_preview` longtext COLLATE utf8mb4_unicode_ci,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `workflow_failures_source_kind_source_id_index` (`source_kind`,`source_id`),\n KEY `workflow_failures_workflow_run_id_index` (`workflow_run_id`),\n KEY `workflow_failures_failure_category_index` (`failure_category`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_history_events", + "CREATE TABLE `workflow_history_events` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `sequence` int unsigned NOT NULL,\n `event_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `payload` longtext COLLATE utf8mb4_bin,\n `workflow_task_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `workflow_command_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `recorded_at` timestamp(6) NULL DEFAULT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n `recorded_at_utc` datetime(6) DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_history_events_workflow_run_id_sequence_unique` (`workflow_run_id`,`sequence`),\n KEY `workflow_history_events_workflow_run_id_index` (`workflow_run_id`),\n KEY `workflow_history_events_workflow_task_id_index` (`workflow_task_id`),\n KEY `workflow_history_events_workflow_command_id_index` (`workflow_command_id`),\n CONSTRAINT `dw_json_text_186a93a5a54b983f2444` CHECK (json_valid(`payload`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_inbound_stream_items", + "CREATE TABLE `workflow_inbound_stream_items` (\n `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n `stream_id` bigint unsigned NOT NULL,\n `namespace` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `stream_name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `message_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `position` bigint unsigned NOT NULL,\n `payload_codec` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,\n `payload_blob` longtext COLLATE utf8mb4_unicode_ci NOT NULL,\n `payload_hash` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `delivered_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `workflow_command_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `delivered_at` timestamp(6) NULL DEFAULT NULL,\n `consumed_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `consumed_task_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `consumed_at` timestamp(6) NULL DEFAULT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n `payload_released_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `wf_inbound_items_stream_message_unique` (`stream_id`,`message_id`),\n UNIQUE KEY `wf_inbound_items_stream_position_unique` (`stream_id`,`position`),\n KEY `wf_inbound_items_ns_instance_name_position_idx` (`namespace`,`workflow_instance_id`,`stream_name`,`position`),\n KEY `workflow_inbound_stream_items_namespace_index` (`namespace`),\n KEY `workflow_inbound_stream_items_workflow_instance_id_index` (`workflow_instance_id`),\n KEY `workflow_inbound_stream_items_delivered_run_id_index` (`delivered_run_id`),\n KEY `workflow_inbound_stream_items_workflow_command_id_index` (`workflow_command_id`),\n KEY `workflow_inbound_stream_items_consumed_run_id_index` (`consumed_run_id`),\n KEY `workflow_inbound_stream_items_consumed_task_id_index` (`consumed_task_id`),\n KEY `workflow_inbound_stream_items_payload_released_at_index` (`payload_released_at`),\n CONSTRAINT `workflow_inbound_stream_items_stream_id_foreign` FOREIGN KEY (`stream_id`) REFERENCES `workflow_inbound_streams` (`id`) ON DELETE CASCADE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_inbound_streams", + "CREATE TABLE `workflow_inbound_streams` (\n `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n `namespace` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `stream_name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `last_position` bigint unsigned NOT NULL DEFAULT '0',\n `cursor_position` bigint unsigned NOT NULL DEFAULT '0',\n `cursor_checkpoint_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `waiting_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `waiting_after_position` bigint unsigned DEFAULT NULL,\n `waiting_since` timestamp(6) NULL DEFAULT NULL,\n `duplicate_count` bigint unsigned NOT NULL DEFAULT '0',\n `malformed_count` bigint unsigned NOT NULL DEFAULT '0',\n `last_input_outcome` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `last_input_message_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `last_input_at` timestamp(6) NULL DEFAULT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n `cleanup_blocked_at` timestamp(6) NULL DEFAULT NULL,\n `cleanup_blocked_reason` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `cleanup_blocked_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `wf_inbound_streams_ns_instance_name_unique` (`namespace`,`workflow_instance_id`,`stream_name`),\n KEY `workflow_inbound_streams_namespace_index` (`namespace`),\n KEY `workflow_inbound_streams_workflow_instance_id_index` (`workflow_instance_id`),\n KEY `workflow_inbound_streams_cursor_checkpoint_run_id_index` (`cursor_checkpoint_run_id`),\n KEY `workflow_inbound_streams_waiting_run_id_index` (`waiting_run_id`),\n KEY `workflow_inbound_streams_cleanup_blocked_run_id_index` (`cleanup_blocked_run_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_instances", + "CREATE TABLE `workflow_instances` (\n `id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_class` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `business_key` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `visibility_labels` longtext COLLATE utf8mb4_bin,\n `memo` longtext COLLATE utf8mb4_bin,\n `execution_timeout_seconds` int unsigned DEFAULT NULL,\n `current_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `run_count` int unsigned NOT NULL DEFAULT '0',\n `last_message_sequence` int unsigned NOT NULL DEFAULT '0',\n `reserved_at` timestamp(6) NULL DEFAULT NULL,\n `started_at` timestamp(6) NULL DEFAULT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `workflow_instances_namespace_index` (`namespace`),\n KEY `workflow_instances_business_key_index` (`business_key`),\n KEY `workflow_instances_current_run_id_index` (`current_run_id`),\n CONSTRAINT `dw_json_text_4d686a7e3879bc8d7c6c` CHECK (json_valid(`memo`)),\n CONSTRAINT `dw_json_text_5dec5cdd6359edc700f2` CHECK (json_valid(`visibility_labels`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_links", + "CREATE TABLE `workflow_links` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `link_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `sequence` int unsigned DEFAULT NULL,\n `parallel_group_path` longtext COLLATE utf8mb4_bin,\n `parent_workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `parent_workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `child_workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `child_workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `is_primary_parent` tinyint(1) NOT NULL DEFAULT '0',\n `parent_close_policy` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'abandon',\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_links_parent_child_type_unique` (`parent_workflow_run_id`,`child_workflow_run_id`,`link_type`),\n KEY `workflow_links_parent_sequence_type_index` (`parent_workflow_run_id`,`sequence`,`link_type`),\n KEY `workflow_links_parent_workflow_instance_id_index` (`parent_workflow_instance_id`),\n KEY `workflow_links_parent_workflow_run_id_index` (`parent_workflow_run_id`),\n KEY `workflow_links_child_workflow_instance_id_index` (`child_workflow_instance_id`),\n KEY `workflow_links_child_workflow_run_id_index` (`child_workflow_run_id`),\n CONSTRAINT `dw_json_text_c6fec96f4176ab012436` CHECK (json_valid(`parallel_group_path`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_memos", + "CREATE TABLE `workflow_memos` (\n `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `key` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `value` longtext COLLATE utf8mb4_bin NOT NULL,\n `upserted_at_sequence` int unsigned NOT NULL,\n `inherited_from_parent` tinyint(1) NOT NULL DEFAULT '0',\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n `portable_value` longtext COLLATE utf8mb4_bin,\n `portable_value_sequence` int unsigned DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_memos_run_key_unique` (`workflow_run_id`,`key`),\n KEY `workflow_memos_instance_key` (`workflow_instance_id`,`key`),\n CONSTRAINT `workflow_memos_workflow_run_id_foreign` FOREIGN KEY (`workflow_run_id`) REFERENCES `workflow_runs` (`id`) ON DELETE CASCADE,\n CONSTRAINT `dw_json_text_71688955589bdaf8f5c4` CHECK (json_valid(`portable_value`)),\n CONSTRAINT `dw_json_text_fcdcfa03c1ad3583628c` CHECK (json_valid(`value`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_messages", + "CREATE TABLE `workflow_messages` (\n `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `direction` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL,\n `channel` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `stream_key` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `sequence` bigint unsigned NOT NULL,\n `source_workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `source_workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `target_workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `target_workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `correlation_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `idempotency_key` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `payload_reference` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `consume_state` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending',\n `consumed_at` timestamp(6) NULL DEFAULT NULL,\n `consumed_by_sequence` int unsigned DEFAULT NULL,\n `expires_at` timestamp(6) NULL DEFAULT NULL,\n `delivery_attempt_count` int unsigned NOT NULL DEFAULT '0',\n `last_delivery_attempt_at` timestamp(6) NULL DEFAULT NULL,\n `last_delivery_error` text COLLATE utf8mb4_unicode_ci,\n `metadata` longtext COLLATE utf8mb4_bin,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `wf_msgs_stream_seq_unique` (`workflow_instance_id`,`stream_key`,`sequence`),\n KEY `wf_msgs_instance_stream_seq` (`workflow_instance_id`,`stream_key`,`sequence`),\n KEY `wf_msgs_run_dir_state` (`workflow_run_id`,`direction`,`consume_state`),\n KEY `wf_msgs_stream_seq_state` (`stream_key`,`sequence`,`consume_state`),\n KEY `wf_msgs_target_state` (`target_workflow_instance_id`,`consume_state`),\n KEY `workflow_messages_workflow_instance_id_index` (`workflow_instance_id`),\n KEY `workflow_messages_workflow_run_id_index` (`workflow_run_id`),\n KEY `workflow_messages_direction_index` (`direction`),\n KEY `workflow_messages_channel_index` (`channel`),\n KEY `workflow_messages_stream_key_index` (`stream_key`),\n KEY `workflow_messages_sequence_index` (`sequence`),\n KEY `workflow_messages_source_workflow_instance_id_index` (`source_workflow_instance_id`),\n KEY `workflow_messages_target_workflow_instance_id_index` (`target_workflow_instance_id`),\n KEY `workflow_messages_correlation_id_index` (`correlation_id`),\n KEY `workflow_messages_idempotency_key_index` (`idempotency_key`),\n KEY `workflow_messages_consume_state_index` (`consume_state`),\n CONSTRAINT `workflow_messages_workflow_run_id_foreign` FOREIGN KEY (`workflow_run_id`) REFERENCES `workflow_runs` (`id`) ON DELETE CASCADE,\n CONSTRAINT `dw_json_text_b0a2ae51e86f4f80b9fb` CHECK (json_valid(`metadata`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_namespaces", + "CREATE TABLE `workflow_namespaces` (\n `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n `name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `description` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `retention_mode` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'bounded',\n `retention_days` int unsigned DEFAULT '30',\n `status` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'active',\n `external_payload_storage` longtext COLLATE utf8mb4_bin,\n `created_at` timestamp NULL DEFAULT NULL,\n `updated_at` timestamp NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_namespaces_name_unique` (`name`),\n CONSTRAINT `dw_json_text_4281ef3e52494643312f` CHECK (json_valid(`external_payload_storage`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_run_lineage_entries", + "CREATE TABLE `workflow_run_lineage_entries` (\n `id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `direction` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `lineage_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `position` int unsigned NOT NULL,\n `link_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `child_call_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `sequence` int unsigned DEFAULT NULL,\n `is_primary_parent` tinyint(1) NOT NULL DEFAULT '0',\n `related_workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `related_workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `related_run_number` int unsigned DEFAULT NULL,\n `related_workflow_type` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `related_workflow_class` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `status` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `status_bucket` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `closed_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `linked_at` timestamp(6) NULL DEFAULT NULL,\n `payload` longtext COLLATE utf8mb4_bin,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_run_lineage_run_direction_lineage_unique` (`workflow_run_id`,`direction`,`lineage_id`),\n KEY `workflow_run_lineage_run_direction_position_index` (`workflow_run_id`,`direction`,`position`),\n KEY `workflow_run_lineage_instance_direction_type_index` (`workflow_instance_id`,`direction`,`link_type`),\n KEY `workflow_run_lineage_entries_workflow_run_id_index` (`workflow_run_id`),\n KEY `workflow_run_lineage_entries_workflow_instance_id_index` (`workflow_instance_id`),\n KEY `workflow_run_lineage_entries_direction_index` (`direction`),\n KEY `workflow_run_lineage_entries_link_type_index` (`link_type`),\n KEY `workflow_run_lineage_entries_child_call_id_index` (`child_call_id`),\n KEY `workflow_run_lineage_entries_sequence_index` (`sequence`),\n KEY `workflow_run_lineage_entries_is_primary_parent_index` (`is_primary_parent`),\n KEY `workflow_run_lineage_entries_related_workflow_instance_id_index` (`related_workflow_instance_id`),\n KEY `workflow_run_lineage_entries_related_workflow_run_id_index` (`related_workflow_run_id`),\n KEY `workflow_run_lineage_entries_related_workflow_type_index` (`related_workflow_type`),\n KEY `workflow_run_lineage_entries_status_index` (`status`),\n KEY `workflow_run_lineage_entries_status_bucket_index` (`status_bucket`),\n KEY `workflow_run_lineage_entries_closed_reason_index` (`closed_reason`),\n KEY `workflow_run_lineage_entries_linked_at_index` (`linked_at`),\n CONSTRAINT `dw_json_text_1874a82e6e4c5698bc68` CHECK (json_valid(`payload`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_run_summaries", + "CREATE TABLE `workflow_run_summaries` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `run_number` int unsigned NOT NULL,\n `is_current_run` tinyint(1) NOT NULL DEFAULT '0',\n `engine_source` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'v2',\n `projection_schema_version` smallint unsigned DEFAULT NULL,\n `class` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `compatibility` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `declared_entry_mode` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `declared_contract_source` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `business_key` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `visibility_labels` longtext COLLATE utf8mb4_bin,\n `status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `status_bucket` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `closed_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `connection` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `queue` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `started_at` timestamp(6) NULL DEFAULT NULL,\n `sort_timestamp` timestamp(6) NULL DEFAULT NULL,\n `sort_key` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `closed_at` timestamp(6) NULL DEFAULT NULL,\n `archived_at` timestamp(6) NULL DEFAULT NULL,\n `archive_command_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `archive_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `duration_ms` bigint DEFAULT NULL,\n `wait_kind` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `wait_reason` text COLLATE utf8mb4_unicode_ci,\n `wait_started_at` timestamp(6) NULL DEFAULT NULL,\n `wait_deadline_at` timestamp(6) NULL DEFAULT NULL,\n `open_wait_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `resume_source_kind` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `resume_source_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `next_task_at` timestamp(6) NULL DEFAULT NULL,\n `liveness_state` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `liveness_reason` text COLLATE utf8mb4_unicode_ci,\n `repair_blocked_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `repair_attention` tinyint(1) NOT NULL DEFAULT '0',\n `task_problem` tinyint(1) NOT NULL DEFAULT '0',\n `next_task_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `next_task_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `next_task_status` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `next_task_lease_expires_at` timestamp(6) NULL DEFAULT NULL,\n `exception_count` int unsigned NOT NULL DEFAULT '0',\n `history_event_count` int unsigned NOT NULL DEFAULT '0',\n `history_size_bytes` bigint unsigned NOT NULL DEFAULT '0',\n `history_fan_out` int unsigned NOT NULL DEFAULT '0',\n `continue_as_new_recommended` tinyint(1) NOT NULL DEFAULT '0',\n `history_budget_pressure` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'ok',\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `workflow_run_summaries_status_bucket_started_at_index` (`status_bucket`,`started_at`),\n KEY `workflow_run_summaries_sort_order_index` (`sort_timestamp`,`id`),\n KEY `wfrs_namespace_sort_idx` (`namespace`,`sort_timestamp`,`id`),\n KEY `wfrs_namespace_type_sort_idx` (`namespace`,`workflow_type`,`sort_timestamp`,`id`),\n KEY `wfrs_namespace_status_sort_idx` (`namespace`,`status_bucket`,`sort_timestamp`,`id`),\n KEY `workflow_run_summaries_sort_key_index` (`sort_key`),\n KEY `workflow_run_summaries_workflow_instance_id_index` (`workflow_instance_id`),\n KEY `workflow_run_summaries_is_current_run_index` (`is_current_run`),\n KEY `workflow_run_summaries_projection_schema_version_index` (`projection_schema_version`),\n KEY `workflow_run_summaries_namespace_index` (`namespace`),\n KEY `wfrs_decl_entry_mode_idx` (`declared_entry_mode`),\n KEY `wfrs_decl_contract_source_idx` (`declared_contract_source`),\n KEY `workflow_run_summaries_business_key_index` (`business_key`),\n KEY `workflow_run_summaries_status_index` (`status`),\n KEY `workflow_run_summaries_status_bucket_index` (`status_bucket`),\n KEY `workflow_run_summaries_archived_at_index` (`archived_at`),\n KEY `workflow_run_summaries_archive_command_id_index` (`archive_command_id`),\n KEY `workflow_run_summaries_repair_blocked_reason_index` (`repair_blocked_reason`),\n KEY `workflow_run_summaries_repair_attention_index` (`repair_attention`),\n KEY `workflow_run_summaries_continue_as_new_recommended_index` (`continue_as_new_recommended`),\n KEY `workflow_run_summaries_history_budget_pressure_index` (`history_budget_pressure`),\n KEY `wfrs_prom_metrics_series_idx` (`namespace`,`queue`,`workflow_type`),\n CONSTRAINT `dw_json_text_accf32ee2c1a832ab845` CHECK (json_valid(`visibility_labels`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_run_timeline_entries", + "CREATE TABLE `workflow_run_timeline_entries` (\n `id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `history_event_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `sequence` int unsigned NOT NULL,\n `type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `kind` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `entry_kind` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'point',\n `source_kind` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `source_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `summary` text COLLATE utf8mb4_unicode_ci,\n `recorded_at` timestamp(6) NULL DEFAULT NULL,\n `command_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `command_sequence` int unsigned DEFAULT NULL,\n `task_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `activity_execution_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `timer_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `failure_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `payload` longtext COLLATE utf8mb4_bin,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_run_timeline_run_event_unique` (`workflow_run_id`,`history_event_id`),\n KEY `workflow_run_timeline_run_sequence_index` (`workflow_run_id`,`sequence`),\n KEY `workflow_run_timeline_instance_kind_recorded_index` (`workflow_instance_id`,`kind`,`recorded_at`),\n KEY `workflow_run_timeline_entries_workflow_run_id_index` (`workflow_run_id`),\n KEY `workflow_run_timeline_entries_workflow_instance_id_index` (`workflow_instance_id`),\n KEY `workflow_run_timeline_entries_history_event_id_index` (`history_event_id`),\n KEY `workflow_run_timeline_entries_sequence_index` (`sequence`),\n KEY `workflow_run_timeline_entries_type_index` (`type`),\n KEY `workflow_run_timeline_entries_kind_index` (`kind`),\n KEY `workflow_run_timeline_entries_source_kind_index` (`source_kind`),\n KEY `workflow_run_timeline_entries_source_id_index` (`source_id`),\n KEY `workflow_run_timeline_entries_recorded_at_index` (`recorded_at`),\n KEY `workflow_run_timeline_entries_command_id_index` (`command_id`),\n KEY `workflow_run_timeline_entries_command_sequence_index` (`command_sequence`),\n KEY `workflow_run_timeline_entries_task_id_index` (`task_id`),\n KEY `workflow_run_timeline_entries_activity_execution_id_index` (`activity_execution_id`),\n KEY `workflow_run_timeline_entries_timer_id_index` (`timer_id`),\n KEY `workflow_run_timeline_entries_failure_id_index` (`failure_id`),\n CONSTRAINT `dw_json_text_4a34bf0686f5464d6b56` CHECK (json_valid(`payload`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_run_timer_entries", + "CREATE TABLE `workflow_run_timer_entries` (\n `id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `timer_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `schema_version` smallint unsigned NOT NULL DEFAULT '1',\n `position` int unsigned NOT NULL,\n `sequence` int unsigned DEFAULT NULL,\n `status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `source_status` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `delay_seconds` int DEFAULT NULL,\n `fire_at` timestamp(6) NULL DEFAULT NULL,\n `fired_at` timestamp(6) NULL DEFAULT NULL,\n `cancelled_at` timestamp(6) NULL DEFAULT NULL,\n `timer_kind` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `condition_wait_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `condition_key` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `condition_definition_fingerprint` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `history_authority` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `history_unsupported_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `payload` longtext COLLATE utf8mb4_bin,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_run_timer_entries_run_timer_unique` (`workflow_run_id`,`timer_id`),\n KEY `workflow_run_timer_entries_run_status_position_index` (`workflow_run_id`,`status`,`position`),\n KEY `workflow_run_timer_entries_instance_kind_status_index` (`workflow_instance_id`,`timer_kind`,`status`),\n KEY `workflow_run_timer_entries_workflow_run_id_index` (`workflow_run_id`),\n KEY `workflow_run_timer_entries_workflow_instance_id_index` (`workflow_instance_id`),\n KEY `workflow_run_timer_entries_sequence_index` (`sequence`),\n KEY `workflow_run_timer_entries_status_index` (`status`),\n KEY `workflow_run_timer_entries_source_status_index` (`source_status`),\n KEY `workflow_run_timer_entries_timer_kind_index` (`timer_kind`),\n KEY `workflow_run_timer_entries_condition_wait_id_index` (`condition_wait_id`),\n KEY `workflow_run_timer_entries_condition_key_index` (`condition_key`),\n KEY `workflow_run_timer_entries_history_authority_index` (`history_authority`),\n CONSTRAINT `dw_json_text_cb1a9145e9dc22308d3d` CHECK (json_valid(`payload`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_run_timers", + "CREATE TABLE `workflow_run_timers` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `sequence` int unsigned NOT NULL,\n `status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `delay_seconds` bigint unsigned NOT NULL,\n `fire_at` timestamp(6) NOT NULL,\n `fired_at` timestamp(6) NULL DEFAULT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_run_timers_workflow_run_id_sequence_unique` (`workflow_run_id`,`sequence`),\n KEY `workflow_run_timers_status_fire_at_index` (`status`,`fire_at`),\n KEY `workflow_run_timers_workflow_run_id_index` (`workflow_run_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_run_waits", + "CREATE TABLE `workflow_run_waits` (\n `id` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `wait_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `position` int unsigned NOT NULL,\n `kind` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `sequence` int unsigned DEFAULT NULL,\n `status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `source_status` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `summary` text COLLATE utf8mb4_unicode_ci,\n `opened_at` timestamp(6) NULL DEFAULT NULL,\n `deadline_at` timestamp(6) NULL DEFAULT NULL,\n `resolved_at` timestamp(6) NULL DEFAULT NULL,\n `target_name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `target_type` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `task_backed` tinyint(1) NOT NULL DEFAULT '0',\n `external_only` tinyint(1) NOT NULL DEFAULT '0',\n `resume_source_kind` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `resume_source_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `task_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `task_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `task_status` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `command_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `command_sequence` int unsigned DEFAULT NULL,\n `command_status` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `command_outcome` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `history_authority` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `history_unsupported_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `payload` longtext COLLATE utf8mb4_bin,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_run_waits_run_wait_unique` (`workflow_run_id`,`wait_id`),\n KEY `workflow_run_waits_run_status_position_index` (`workflow_run_id`,`status`,`position`),\n KEY `workflow_run_waits_instance_kind_status_index` (`workflow_instance_id`,`kind`,`status`),\n KEY `workflow_run_waits_workflow_run_id_index` (`workflow_run_id`),\n KEY `workflow_run_waits_workflow_instance_id_index` (`workflow_instance_id`),\n KEY `workflow_run_waits_kind_index` (`kind`),\n KEY `workflow_run_waits_sequence_index` (`sequence`),\n KEY `workflow_run_waits_status_index` (`status`),\n KEY `workflow_run_waits_source_status_index` (`source_status`),\n KEY `workflow_run_waits_target_name_index` (`target_name`),\n KEY `workflow_run_waits_task_backed_index` (`task_backed`),\n KEY `workflow_run_waits_resume_source_kind_index` (`resume_source_kind`),\n KEY `workflow_run_waits_resume_source_id_index` (`resume_source_id`),\n KEY `workflow_run_waits_task_id_index` (`task_id`),\n KEY `workflow_run_waits_command_id_index` (`command_id`),\n KEY `workflow_run_waits_history_authority_index` (`history_authority`),\n CONSTRAINT `dw_json_text_e331c14bcddf551ccfdd` CHECK (json_valid(`payload`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_runs", + "CREATE TABLE `workflow_runs` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `run_number` int unsigned NOT NULL,\n `workflow_class` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `business_key` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `visibility_labels` longtext COLLATE utf8mb4_bin,\n `status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `closed_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `compatibility` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `payload_codec` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `arguments` longtext COLLATE utf8mb4_unicode_ci,\n `output` longtext COLLATE utf8mb4_unicode_ci,\n `output_payload_codec` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `connection` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `queue` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `priority` tinyint unsigned NOT NULL DEFAULT '5',\n `fairness_key` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `fairness_weight` smallint unsigned NOT NULL DEFAULT '1',\n `sticky_worker_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `sticky_until` timestamp(6) NULL DEFAULT NULL,\n `last_history_sequence` int unsigned NOT NULL DEFAULT '0',\n `last_command_sequence` int unsigned NOT NULL DEFAULT '0',\n `message_cursor_position` int unsigned NOT NULL DEFAULT '0',\n `run_timeout_seconds` int unsigned DEFAULT NULL,\n `execution_deadline_at` timestamp(6) NULL DEFAULT NULL,\n `run_deadline_at` timestamp(6) NULL DEFAULT NULL,\n `started_at` timestamp(6) NULL DEFAULT NULL,\n `closed_at` timestamp(6) NULL DEFAULT NULL,\n `archived_at` timestamp(6) NULL DEFAULT NULL,\n `archive_command_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `archive_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `last_progress_at` timestamp(6) NULL DEFAULT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n `import_source` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `import_id` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `import_dedupe_key` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `import_contract_version` smallint unsigned DEFAULT NULL,\n `imported_at` timestamp(6) NULL DEFAULT NULL,\n `cancellation_request_command_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `cancellation_requested_at` timestamp(6) NULL DEFAULT NULL,\n `cancellation_deadline_at` timestamp(6) NULL DEFAULT NULL,\n `cancellation_delivery_sequence` int unsigned DEFAULT NULL,\n `cancellation_delivered_at` timestamp(6) NULL DEFAULT NULL,\n `details_pruned_at` timestamp(6) NULL DEFAULT NULL,\n `cancellation_scope_recovery_until` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_runs_workflow_instance_id_run_number_unique` (`workflow_instance_id`,`run_number`),\n KEY `workflow_runs_workflow_instance_id_index` (`workflow_instance_id`),\n KEY `workflow_runs_namespace_index` (`namespace`),\n KEY `workflow_runs_business_key_index` (`business_key`),\n KEY `workflow_runs_sticky_worker_id_index` (`sticky_worker_id`),\n KEY `workflow_runs_sticky_until_index` (`sticky_until`),\n KEY `workflow_runs_archived_at_index` (`archived_at`),\n KEY `workflow_runs_archive_command_id_index` (`archive_command_id`),\n KEY `workflow_runs_import_source_index` (`import_source`),\n KEY `workflow_runs_import_id_index` (`import_id`),\n KEY `workflow_runs_import_dedupe_key_index` (`import_dedupe_key`),\n KEY `workflow_runs_imported_at_index` (`imported_at`),\n KEY `workflow_runs_prom_metrics_queue_idx` (`namespace`,`queue`),\n KEY `workflow_runs_namespace_status_quota_idx` (`namespace`,`status`),\n KEY `workflow_runs_cancellation_request_command_id_index` (`cancellation_request_command_id`),\n CONSTRAINT `dw_json_text_0cabe9701a2ae7cb549e` CHECK (json_valid(`visibility_labels`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_schedule_history_events", + "CREATE TABLE `workflow_schedule_history_events` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_schedule_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `schedule_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `sequence` int unsigned NOT NULL,\n `event_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `payload` longtext COLLATE utf8mb4_bin,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `recorded_at` timestamp(6) NULL DEFAULT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n `occurrence_at_utc` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `wf_schedule_history_schedule_sequence_unique` (`workflow_schedule_id`,`sequence`),\n KEY `wf_schedule_history_namespace_schedule_idx` (`namespace`,`schedule_id`),\n KEY `wf_schedule_history_event_recorded_idx` (`event_type`,`recorded_at`),\n KEY `wf_schedule_history_workflow_schedule_idx` (`workflow_schedule_id`),\n KEY `wf_schedule_history_schedule_idx` (`schedule_id`),\n KEY `wf_schedule_history_namespace_idx` (`namespace`),\n KEY `wf_schedule_history_instance_idx` (`workflow_instance_id`),\n KEY `wf_schedule_history_run_idx` (`workflow_run_id`),\n KEY `wf_schedule_history_occurrence_idx` (`workflow_schedule_id`,`event_type`,`occurrence_at_utc`),\n CONSTRAINT `dw_json_text_5c5faf594e8d31ebe991` CHECK (json_valid(`payload`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_schedules", + "CREATE TABLE `workflow_schedules` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `schedule_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `spec` longtext COLLATE utf8mb4_bin,\n `action` longtext COLLATE utf8mb4_bin,\n `status` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'active',\n `overlap_policy` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'skip',\n `note` text COLLATE utf8mb4_unicode_ci,\n `memo` longtext COLLATE utf8mb4_bin,\n `search_attributes` longtext COLLATE utf8mb4_bin,\n `visibility_labels` longtext COLLATE utf8mb4_bin,\n `jitter_seconds` int unsigned NOT NULL DEFAULT '0',\n `max_runs` bigint unsigned DEFAULT NULL,\n `remaining_actions` bigint unsigned DEFAULT NULL,\n `fires_count` bigint unsigned NOT NULL DEFAULT '0',\n `failures_count` bigint unsigned NOT NULL DEFAULT '0',\n `recent_actions` longtext COLLATE utf8mb4_bin,\n `buffered_actions` longtext COLLATE utf8mb4_bin,\n `last_fired_at` timestamp(6) NULL DEFAULT NULL,\n `next_fire_at` timestamp(6) NULL DEFAULT NULL,\n `latest_workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `connection` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `queue` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `paused_at` timestamp(6) NULL DEFAULT NULL,\n `deleted_at` timestamp(6) NULL DEFAULT NULL,\n `last_skip_reason` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `last_skipped_at` timestamp(6) NULL DEFAULT NULL,\n `skipped_trigger_count` bigint unsigned NOT NULL DEFAULT '0',\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_schedules_namespace_schedule_id_unique` (`namespace`,`schedule_id`),\n KEY `workflow_schedules_status_next_fire_at_index` (`status`,`next_fire_at`),\n KEY `workflow_schedules_namespace_index` (`namespace`),\n KEY `workflow_schedules_latest_workflow_instance_id_index` (`latest_workflow_instance_id`),\n CONSTRAINT `dw_json_text_023daf97483a6925003e` CHECK (json_valid(`recent_actions`)),\n CONSTRAINT `dw_json_text_0779e5110faa8bb3a1ce` CHECK (json_valid(`action`)),\n CONSTRAINT `dw_json_text_127f3a5484eae46b4296` CHECK (json_valid(`memo`)),\n CONSTRAINT `dw_json_text_260b502951aabe6967a2` CHECK (json_valid(`visibility_labels`)),\n CONSTRAINT `dw_json_text_348ceaaa72578f94b30d` CHECK (json_valid(`search_attributes`)),\n CONSTRAINT `dw_json_text_d7aa273e112ccdb9bce4` CHECK (json_valid(`spec`)),\n CONSTRAINT `dw_json_text_fcbf8efdd61dbed85da4` CHECK (json_valid(`buffered_actions`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_search_attributes", + "CREATE TABLE `workflow_search_attributes` (\n `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `key` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Attribute name (e.g., \"customer_id\", \"priority\", \"region\")',\n `type` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Type: string, keyword, int, float, bool, datetime',\n `value_string` text COLLATE utf8mb4_unicode_ci COMMENT 'For type=string (max 2048 chars enforced at app level)',\n `value_keyword` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'For type=keyword (exact match, indexed)',\n `value_keyword_list` longtext COLLATE utf8mb4_bin COMMENT 'For type=keyword_list (ordered exact-match values)',\n `value_int` bigint DEFAULT NULL COMMENT 'For type=int',\n `value_float` double DEFAULT NULL COMMENT 'For type=float',\n `value_bool` tinyint(1) DEFAULT NULL COMMENT 'For type=bool',\n `value_datetime` timestamp(6) NULL DEFAULT NULL COMMENT 'For type=datetime (microsecond precision)',\n `upserted_at_sequence` int unsigned NOT NULL COMMENT 'History sequence when this attribute was last upserted',\n `inherited_from_parent` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'True if inherited via continue-as-new',\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_search_attrs_run_key_unique` (`workflow_run_id`,`key`),\n KEY `workflow_search_attrs_instance_key_type` (`workflow_instance_id`,`key`,`type`),\n KEY `workflow_search_attrs_key_keyword` (`key`,`value_keyword`),\n KEY `workflow_search_attrs_key_int` (`key`,`value_int`),\n KEY `workflow_search_attrs_key_float` (`key`,`value_float`),\n KEY `workflow_search_attrs_key_bool` (`key`,`value_bool`),\n KEY `workflow_search_attrs_key_datetime` (`key`,`value_datetime`),\n KEY `workflow_search_attributes_workflow_run_id_index` (`workflow_run_id`),\n KEY `workflow_search_attributes_workflow_instance_id_index` (`workflow_instance_id`),\n KEY `workflow_search_attributes_value_keyword_index` (`value_keyword`),\n KEY `workflow_search_attributes_value_int_index` (`value_int`),\n KEY `workflow_search_attributes_value_float_index` (`value_float`),\n KEY `workflow_search_attributes_value_bool_index` (`value_bool`),\n KEY `workflow_search_attributes_value_datetime_index` (`value_datetime`),\n CONSTRAINT `workflow_search_attributes_workflow_run_id_foreign` FOREIGN KEY (`workflow_run_id`) REFERENCES `workflow_runs` (`id`) ON DELETE CASCADE,\n CONSTRAINT `dw_json_text_df0541f7f01fef92919f` CHECK (json_valid(`value_keyword_list`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_service_calls", + "CREATE TABLE `workflow_service_calls` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_service_endpoint_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `workflow_service_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `workflow_service_operation_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `namespace` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `endpoint_name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `service_name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `operation_name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `caller_namespace` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `caller_workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `caller_workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `target_namespace` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `linked_workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `linked_workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `linked_workflow_update_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `status` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,\n `outcome` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `operation_mode` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,\n `resolved_binding_kind` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `resolved_target_reference` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `payload_codec` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `input_payload_reference` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `output_payload_reference` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `failure_payload_reference` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `failure_message` text COLLATE utf8mb4_unicode_ci,\n `idempotency_key` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `deadline_policy` longtext COLLATE utf8mb4_bin,\n `idempotency_policy` longtext COLLATE utf8mb4_bin,\n `cancellation_policy` longtext COLLATE utf8mb4_bin,\n `retry_policy` longtext COLLATE utf8mb4_bin,\n `boundary_policy` longtext COLLATE utf8mb4_bin,\n `metadata` longtext COLLATE utf8mb4_bin,\n `outcome_category` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `outcome_reason` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `outcome_message` text COLLATE utf8mb4_unicode_ci,\n `outcome_metadata` longtext COLLATE utf8mb4_bin,\n `policy_name` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `retry_after_seconds` int unsigned DEFAULT NULL,\n `caller_principal_subject` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `caller_principal_method` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `caller_principal_roles` longtext COLLATE utf8mb4_bin,\n `caller_principal_tenant` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `caller_principal_claims` longtext COLLATE utf8mb4_bin,\n `accepted_at` timestamp(6) NULL DEFAULT NULL,\n `started_at` timestamp(6) NULL DEFAULT NULL,\n `completed_at` timestamp(6) NULL DEFAULT NULL,\n `failed_at` timestamp(6) NULL DEFAULT NULL,\n `cancelled_at` timestamp(6) NULL DEFAULT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `wf_service_calls_namespace_status_idx` (`namespace`,`status`),\n KEY `wf_service_calls_caller_status_idx` (`caller_namespace`,`status`),\n KEY `wf_service_calls_target_status_idx` (`target_namespace`,`status`),\n KEY `wf_service_calls_namespace_outcome_idx` (`namespace`,`outcome`),\n KEY `wf_service_calls_caller_outcome_idx` (`caller_namespace`,`outcome`),\n KEY `wf_service_calls_target_outcome_idx` (`target_namespace`,`outcome`),\n KEY `wf_service_calls_endpoint_idx` (`workflow_service_endpoint_id`),\n KEY `wf_service_calls_service_idx` (`workflow_service_id`),\n KEY `wf_service_calls_operation_idx` (`workflow_service_operation_id`),\n KEY `wf_service_calls_namespace_idx` (`namespace`),\n KEY `wf_service_calls_caller_namespace_idx` (`caller_namespace`),\n KEY `wf_service_calls_caller_instance_idx` (`caller_workflow_instance_id`),\n KEY `wf_service_calls_caller_run_idx` (`caller_workflow_run_id`),\n KEY `wf_service_calls_target_namespace_idx` (`target_namespace`),\n KEY `wf_service_calls_linked_instance_idx` (`linked_workflow_instance_id`),\n KEY `wf_service_calls_linked_run_idx` (`linked_workflow_run_id`),\n KEY `wf_service_calls_linked_update_idx` (`linked_workflow_update_id`),\n KEY `wf_service_calls_status_idx` (`status`),\n KEY `wf_service_calls_outcome_idx` (`outcome`),\n KEY `wf_service_calls_mode_idx` (`operation_mode`),\n KEY `wf_service_calls_binding_kind_idx` (`resolved_binding_kind`),\n KEY `wf_service_calls_idempotency_idx` (`idempotency_key`),\n KEY `wf_service_calls_outcome_category_idx` (`outcome_category`),\n KEY `wf_service_calls_policy_idx` (`policy_name`),\n KEY `wf_service_calls_principal_subject_idx` (`caller_principal_subject`),\n KEY `wf_service_calls_accepted_at_idx` (`accepted_at`),\n CONSTRAINT `dw_json_text_4a6c5e40752d4ed0bdb4` CHECK (json_valid(`metadata`)),\n CONSTRAINT `dw_json_text_5a480bd9165f2eb68e43` CHECK (json_valid(`caller_principal_roles`)),\n CONSTRAINT `dw_json_text_718533c1e82804b31504` CHECK (json_valid(`deadline_policy`)),\n CONSTRAINT `dw_json_text_77a64a96c9d03d965701` CHECK (json_valid(`cancellation_policy`)),\n CONSTRAINT `dw_json_text_89af2bddb6216607276e` CHECK (json_valid(`idempotency_policy`)),\n CONSTRAINT `dw_json_text_89f01ea9712aa07575bf` CHECK (json_valid(`retry_policy`)),\n CONSTRAINT `dw_json_text_b4ffcdd589152c314969` CHECK (json_valid(`outcome_metadata`)),\n CONSTRAINT `dw_json_text_cb4fc45f46fe05d68f99` CHECK (json_valid(`caller_principal_claims`)),\n CONSTRAINT `dw_json_text_e06e9f587b4efc8ce3e9` CHECK (json_valid(`boundary_policy`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_service_endpoints", + "CREATE TABLE `workflow_service_endpoints` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `endpoint_name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `description` text COLLATE utf8mb4_unicode_ci,\n `boundary_policy` longtext COLLATE utf8mb4_bin,\n `metadata` longtext COLLATE utf8mb4_bin,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `wf_service_endpoints_namespace_name_unique` (`namespace`,`endpoint_name`),\n KEY `wf_service_endpoints_namespace_idx` (`namespace`),\n KEY `wf_service_endpoints_name_idx` (`endpoint_name`),\n CONSTRAINT `dw_json_text_3c3c6c7139ec6ffc9fd5` CHECK (json_valid(`metadata`)),\n CONSTRAINT `dw_json_text_6e65cde38938c5bf2752` CHECK (json_valid(`boundary_policy`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_service_operations", + "CREATE TABLE `workflow_service_operations` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_service_endpoint_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_service_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `operation_name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `description` text COLLATE utf8mb4_unicode_ci,\n `operation_mode` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,\n `handler_binding_kind` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `handler_target_reference` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `handler_binding` longtext COLLATE utf8mb4_bin,\n `deadline_policy` longtext COLLATE utf8mb4_bin,\n `idempotency_policy` longtext COLLATE utf8mb4_bin,\n `cancellation_policy` longtext COLLATE utf8mb4_bin,\n `retry_policy` longtext COLLATE utf8mb4_bin,\n `boundary_policy` longtext COLLATE utf8mb4_bin,\n `metadata` longtext COLLATE utf8mb4_bin,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `wf_service_ops_namespace_service_name_unique` (`namespace`,`workflow_service_id`,`operation_name`),\n KEY `wf_service_ops_namespace_name_idx` (`namespace`,`operation_name`),\n KEY `wf_service_ops_endpoint_idx` (`workflow_service_endpoint_id`),\n KEY `wf_service_ops_service_idx` (`workflow_service_id`),\n KEY `wf_service_ops_namespace_idx` (`namespace`),\n KEY `wf_service_ops_name_idx` (`operation_name`),\n KEY `wf_service_ops_mode_idx` (`operation_mode`),\n KEY `wf_service_ops_binding_kind_idx` (`handler_binding_kind`),\n CONSTRAINT `dw_json_text_05dd84f339d72c2ea677` CHECK (json_valid(`boundary_policy`)),\n CONSTRAINT `dw_json_text_13e161b371e6bd18c390` CHECK (json_valid(`cancellation_policy`)),\n CONSTRAINT `dw_json_text_2409229a6279aa76321a` CHECK (json_valid(`retry_policy`)),\n CONSTRAINT `dw_json_text_818561935c015a8f258c` CHECK (json_valid(`metadata`)),\n CONSTRAINT `dw_json_text_aaf235f8970ff7618e0c` CHECK (json_valid(`deadline_policy`)),\n CONSTRAINT `dw_json_text_e844533982a5ba5f974a` CHECK (json_valid(`handler_binding`)),\n CONSTRAINT `dw_json_text_e9b84c12c63f3f05d4f8` CHECK (json_valid(`idempotency_policy`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_services", + "CREATE TABLE `workflow_services` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_service_endpoint_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `service_name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `description` text COLLATE utf8mb4_unicode_ci,\n `boundary_policy` longtext COLLATE utf8mb4_bin,\n `metadata` longtext COLLATE utf8mb4_bin,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `wf_services_namespace_endpoint_name_unique` (`namespace`,`workflow_service_endpoint_id`,`service_name`),\n KEY `wf_services_namespace_name_idx` (`namespace`,`service_name`),\n KEY `wf_services_endpoint_idx` (`workflow_service_endpoint_id`),\n KEY `wf_services_namespace_idx` (`namespace`),\n KEY `wf_services_name_idx` (`service_name`),\n CONSTRAINT `dw_json_text_1af5bae7acfe0b9d3f11` CHECK (json_valid(`boundary_policy`)),\n CONSTRAINT `dw_json_text_4e612803d0ef9c0cfcaa` CHECK (json_valid(`metadata`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_signal_records", + "CREATE TABLE `workflow_signal_records` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_command_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `target_scope` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'instance',\n `requested_workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `resolved_workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `signal_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `signal_wait_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `outcome` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `command_sequence` int unsigned DEFAULT NULL,\n `workflow_sequence` int unsigned DEFAULT NULL,\n `payload_codec` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `arguments` longtext COLLATE utf8mb4_unicode_ci,\n `validation_errors` longtext COLLATE utf8mb4_bin,\n `rejection_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `received_at` timestamp(6) NULL DEFAULT NULL,\n `applied_at` timestamp(6) NULL DEFAULT NULL,\n `rejected_at` timestamp(6) NULL DEFAULT NULL,\n `closed_at` timestamp(6) NULL DEFAULT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_signal_records_workflow_command_id_unique` (`workflow_command_id`),\n KEY `workflow_signal_records_workflow_instance_id_index` (`workflow_instance_id`),\n KEY `workflow_signal_records_workflow_run_id_index` (`workflow_run_id`),\n KEY `workflow_signal_records_requested_workflow_run_id_index` (`requested_workflow_run_id`),\n KEY `workflow_signal_records_resolved_workflow_run_id_index` (`resolved_workflow_run_id`),\n KEY `workflow_signal_records_signal_name_index` (`signal_name`),\n KEY `workflow_signal_records_signal_wait_id_index` (`signal_wait_id`),\n KEY `workflow_signal_records_status_index` (`status`),\n KEY `workflow_signal_records_outcome_index` (`outcome`),\n KEY `workflow_signal_records_command_sequence_index` (`command_sequence`),\n KEY `workflow_signal_records_workflow_sequence_index` (`workflow_sequence`),\n CONSTRAINT `dw_json_text_87990e1e6c171fd7edb3` CHECK (json_valid(`validation_errors`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_task_poll_cursors", + "CREATE TABLE `workflow_task_poll_cursors` (\n `namespace` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `task_queue` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `next_task_kind` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`namespace`,`task_queue`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_tasks", + "CREATE TABLE `workflow_tasks` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `task_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `compatibility` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `payload` longtext COLLATE utf8mb4_bin,\n `connection` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `queue` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `priority` tinyint unsigned NOT NULL DEFAULT '5',\n `fairness_key` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `fairness_weight` smallint unsigned NOT NULL DEFAULT '1',\n `sticky_worker_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `sticky_until` timestamp(6) NULL DEFAULT NULL,\n `sticky_replay_mode` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `sticky_claimed_at` timestamp(6) NULL DEFAULT NULL,\n `available_at` timestamp(6) NULL DEFAULT NULL,\n `leased_at` timestamp(6) NULL DEFAULT NULL,\n `lease_owner` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `lease_expires_at` timestamp(6) NULL DEFAULT NULL,\n `attempt_count` int unsigned NOT NULL DEFAULT '0',\n `last_dispatch_attempt_at` timestamp(6) NULL DEFAULT NULL,\n `last_dispatched_at` timestamp(6) NULL DEFAULT NULL,\n `last_dispatch_error` text COLLATE utf8mb4_unicode_ci,\n `last_claim_failed_at` timestamp(6) NULL DEFAULT NULL,\n `last_claim_error` text COLLATE utf8mb4_unicode_ci,\n `repair_count` int unsigned NOT NULL DEFAULT '0',\n `repair_available_at` timestamp(6) NULL DEFAULT NULL,\n `last_error` text COLLATE utf8mb4_unicode_ci,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n `ready_origin_at` datetime(6) DEFAULT NULL,\n PRIMARY KEY (`id`),\n KEY `workflow_tasks_status_available_at_index` (`status`,`available_at`),\n KEY `workflow_tasks_workflow_run_id_index` (`workflow_run_id`),\n KEY `workflow_tasks_namespace_index` (`namespace`),\n KEY `workflow_tasks_sticky_worker_id_index` (`sticky_worker_id`),\n KEY `workflow_tasks_sticky_until_index` (`sticky_until`),\n KEY `workflow_tasks_sticky_replay_mode_index` (`sticky_replay_mode`),\n KEY `workflow_tasks_sticky_claimed_at_index` (`sticky_claimed_at`),\n KEY `workflow_tasks_dispatch_order_index` (`queue`,`status`,`priority`,`available_at`),\n KEY `workflow_tasks_namespace_queue_status_idx` (`namespace`,`queue`,`status`),\n KEY `workflow_tasks_fairness_class_index` (`queue`,`status`,`fairness_key`),\n KEY `workflow_tasks_prom_metrics_queue_idx` (`namespace`,`queue`),\n KEY `dw_workflow_tasks_poll` (`namespace`(64),`queue`(64),`task_type`(16),`status`(16),`available_at`),\n KEY `dw_workflow_tasks_scoped_dispatch` (`namespace`,`queue`,`task_type`(16),`status`(16),`priority`,`available_at`,`id`),\n CONSTRAINT `dw_json_text_edce43b6b7b815300bd1` CHECK (json_valid(`payload`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_update_validation_tasks", + "CREATE TABLE `workflow_update_validation_tasks` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `idempotency_key` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `task_queue` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `compatibility` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `workflow_definition_fingerprint` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `update_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `request_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `input_hash` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `payload_codec` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `arguments` longtext COLLATE utf8mb4_unicode_ci NOT NULL,\n `command_context` longtext COLLATE utf8mb4_bin,\n `status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `attempt_count` int unsigned NOT NULL DEFAULT '0',\n `lease_owner` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `lease_expires_at` timestamp(6) NULL DEFAULT NULL,\n `rejection_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `rejection_message` text COLLATE utf8mb4_unicode_ci,\n `failure_type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `validation_errors` longtext COLLATE utf8mb4_bin,\n `approved_at` timestamp(6) NULL DEFAULT NULL,\n `rejected_at` timestamp(6) NULL DEFAULT NULL,\n `failed_at` timestamp(6) NULL DEFAULT NULL,\n `timed_out_at` timestamp(6) NULL DEFAULT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_update_validation_tasks_idempotency_key_unique` (`idempotency_key`),\n KEY `workflow_update_validation_poll_index` (`namespace`,`task_queue`,`status`,`created_at`),\n KEY `workflow_update_validation_tasks_namespace_index` (`namespace`),\n KEY `workflow_update_validation_tasks_workflow_instance_id_index` (`workflow_instance_id`),\n KEY `workflow_update_validation_tasks_workflow_run_id_index` (`workflow_run_id`),\n KEY `workflow_update_validation_tasks_workflow_type_index` (`workflow_type`),\n KEY `workflow_update_validation_tasks_task_queue_index` (`task_queue`),\n KEY `workflow_update_validation_tasks_compatibility_index` (`compatibility`),\n KEY `workflow_update_validation_tasks_update_name_index` (`update_name`),\n KEY `workflow_update_validation_tasks_status_index` (`status`),\n KEY `workflow_update_validation_tasks_lease_owner_index` (`lease_owner`),\n KEY `workflow_update_validation_tasks_lease_expires_at_index` (`lease_expires_at`),\n CONSTRAINT `dw_json_text_8648c83f3febc7faec99` CHECK (json_valid(`command_context`)),\n CONSTRAINT `dw_json_text_fc8e8804602cda328168` CHECK (json_valid(`validation_errors`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_updates", + "CREATE TABLE `workflow_updates` (\n `id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_command_id` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,\n `workflow_instance_id` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `target_scope` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'instance',\n `requested_workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `resolved_workflow_run_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `update_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `status` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `outcome` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `command_sequence` int unsigned DEFAULT NULL,\n `workflow_sequence` int unsigned DEFAULT NULL,\n `payload_codec` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `arguments` longtext COLLATE utf8mb4_unicode_ci,\n `result` longtext COLLATE utf8mb4_unicode_ci,\n `validation_errors` longtext COLLATE utf8mb4_bin,\n `rejection_reason` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `failure_id` varchar(26) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `failure_message` text COLLATE utf8mb4_unicode_ci,\n `accepted_at` timestamp(6) NULL DEFAULT NULL,\n `applied_at` timestamp(6) NULL DEFAULT NULL,\n `rejected_at` timestamp(6) NULL DEFAULT NULL,\n `closed_at` timestamp(6) NULL DEFAULT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_updates_workflow_command_id_unique` (`workflow_command_id`),\n KEY `workflow_updates_workflow_instance_id_index` (`workflow_instance_id`),\n KEY `workflow_updates_workflow_run_id_index` (`workflow_run_id`),\n KEY `workflow_updates_requested_workflow_run_id_index` (`requested_workflow_run_id`),\n KEY `workflow_updates_resolved_workflow_run_id_index` (`resolved_workflow_run_id`),\n KEY `workflow_updates_update_name_index` (`update_name`),\n KEY `workflow_updates_status_index` (`status`),\n KEY `workflow_updates_outcome_index` (`outcome`),\n KEY `workflow_updates_command_sequence_index` (`command_sequence`),\n KEY `workflow_updates_workflow_sequence_index` (`workflow_sequence`),\n KEY `workflow_updates_failure_id_index` (`failure_id`),\n CONSTRAINT `dw_json_text_efe19529950144852322` CHECK (json_valid(`validation_errors`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_worker_build_id_rollouts", + "CREATE TABLE `workflow_worker_build_id_rollouts` (\n `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n `namespace` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `task_queue` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `build_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',\n `drain_intent` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'active',\n `drained_at` timestamp NULL DEFAULT NULL,\n `created_at` timestamp NULL DEFAULT NULL,\n `updated_at` timestamp NULL DEFAULT NULL,\n `promoted_at` timestamp NULL DEFAULT NULL,\n `rolled_back_at` timestamp NULL DEFAULT NULL,\n `required_compatibility` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `recorded_fingerprint` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `compatibility_policy` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `workflow_types` longtext COLLATE utf8mb4_bin,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_build_id_rollouts_scope_unique` (`namespace`,`task_queue`,`build_id`),\n CONSTRAINT `dw_json_text_19de9b23ee283257b769` CHECK (json_valid(`workflow_types`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_worker_compatibility_heartbeats", + "CREATE TABLE `workflow_worker_compatibility_heartbeats` (\n `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n `worker_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `scope_key` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `host` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `process_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `connection` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `queue` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `supported` longtext COLLATE utf8mb4_bin NOT NULL,\n `recorded_at` datetime(6) NOT NULL,\n `expires_at` datetime(6) NOT NULL,\n `created_at` timestamp(6) NULL DEFAULT NULL,\n `updated_at` timestamp(6) NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_worker_compatibility_heartbeats_scope_unique` (`worker_id`,`scope_key`),\n KEY `workflow_worker_compatibility_heartbeats_namespace_index` (`namespace`),\n KEY `workflow_worker_compatibility_heartbeats_connection_index` (`connection`),\n KEY `workflow_worker_compatibility_heartbeats_queue_index` (`queue`),\n KEY `workflow_worker_compatibility_heartbeats_expires_at_index` (`expires_at`),\n CONSTRAINT `dw_json_text_c9c8573bb83f23ce864d` CHECK (json_valid(`supported`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_worker_registrations", + "CREATE TABLE `workflow_worker_registrations` (\n `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n `worker_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `namespace` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `task_queue` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `runtime` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,\n `sdk_version` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `build_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `supported_workflow_types` longtext COLLATE utf8mb4_bin,\n `workflow_definition_fingerprints` longtext COLLATE utf8mb4_bin,\n `workflow_command_contracts` longtext COLLATE utf8mb4_bin,\n `supported_activity_types` longtext COLLATE utf8mb4_bin,\n `capabilities` longtext COLLATE utf8mb4_bin,\n `capability_manifest` longtext COLLATE utf8mb4_bin,\n `max_concurrent_workflow_tasks` int unsigned NOT NULL DEFAULT '100',\n `max_concurrent_activity_tasks` int unsigned NOT NULL DEFAULT '100',\n `max_concurrent_worker_sessions` int unsigned NOT NULL DEFAULT '10',\n `available_workflow_slots` int unsigned DEFAULT NULL,\n `available_activity_slots` int unsigned DEFAULT NULL,\n `available_session_slots` int unsigned DEFAULT NULL,\n `process_metrics` longtext COLLATE utf8mb4_bin,\n `heartbeat_interval_seconds` int unsigned DEFAULT NULL,\n `last_heartbeat_at` timestamp NULL DEFAULT NULL,\n `status` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'active',\n `created_at` timestamp NULL DEFAULT NULL,\n `updated_at` timestamp NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_worker_registrations_worker_id_namespace_unique` (`worker_id`,`namespace`),\n KEY `workflow_worker_registrations_namespace_task_queue_status_index` (`namespace`,`task_queue`,`status`),\n CONSTRAINT `dw_json_text_34d29bf4c212ababa706` CHECK (json_valid(`process_metrics`)),\n CONSTRAINT `dw_json_text_4d23abbe7d6f10d67024` CHECK (json_valid(`supported_activity_types`)),\n CONSTRAINT `dw_json_text_4f49aa63cb080bc75d50` CHECK (json_valid(`workflow_definition_fingerprints`)),\n CONSTRAINT `dw_json_text_5db648194e26be45f445` CHECK (json_valid(`capabilities`)),\n CONSTRAINT `dw_json_text_5ff38312fcf7d855445d` CHECK (json_valid(`workflow_command_contracts`)),\n CONSTRAINT `dw_json_text_9e5445d614888bb3661c` CHECK (json_valid(`supported_workflow_types`)),\n CONSTRAINT `dw_json_text_d1357dfe1fe52c7b2691` CHECK (json_valid(`capability_manifest`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ], + [ + "workflow_worker_sessions", + "CREATE TABLE `workflow_worker_sessions` (\n `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n `namespace` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,\n `session_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n `connection` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `queue` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `requirements` longtext COLLATE utf8mb4_bin,\n `status` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'active',\n `lease_owner` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `lease_expires_at` timestamp NULL DEFAULT NULL,\n `ttl_expires_at` timestamp NULL DEFAULT NULL,\n `closed_at` timestamp NULL DEFAULT NULL,\n `failure_reason` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n `lease_seconds` int unsigned NOT NULL DEFAULT '120',\n `ttl_seconds` int unsigned NOT NULL DEFAULT '1800',\n `max_concurrent_activities` int unsigned NOT NULL DEFAULT '1',\n `create_if_missing` tinyint(1) NOT NULL DEFAULT '1',\n `allow_reacquire_after_failure` tinyint(1) NOT NULL DEFAULT '1',\n `last_heartbeat_at` timestamp NULL DEFAULT NULL,\n `created_at` timestamp NULL DEFAULT NULL,\n `updated_at` timestamp NULL DEFAULT NULL,\n PRIMARY KEY (`id`),\n UNIQUE KEY `workflow_worker_sessions_namespace_session_id_unique` (`namespace`,`session_id`),\n KEY `workflow_worker_sessions_namespace_status_index` (`namespace`,`status`),\n KEY `workflow_worker_sessions_namespace_lease_owner_status_index` (`namespace`,`lease_owner`,`status`),\n KEY `workflow_worker_sessions_namespace_queue_status_index` (`namespace`,`queue`,`status`),\n CONSTRAINT `dw_json_text_177d4c6e72d9b9fdebda` CHECK (json_valid(`requirements`))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" + ] +] diff --git a/rust/migrations/mysql/0013_json_text.sql b/rust/migrations/mysql/0013_json_text.sql new file mode 100644 index 000000000..861a9ba9f --- /dev/null +++ b/rust/migrations/mysql/0013_json_text.sql @@ -0,0 +1,192 @@ +-- Native schema 13 on MySQL only (server#1016): MySQL's json type stores +-- doubles in its own binary form and can round them. Store each JSON +-- document as exact utf8mb4_bin text instead, as MariaDB does, with an +-- enforced json_valid check. Each ALTER rebuilds one table; MySQL 8 DDL is +-- atomic per statement, so an interrupted upgrade resumes table by table. +-- MariaDB, PostgreSQL and SQLite are unchanged. +ALTER TABLE `activity_executions` + MODIFY COLUMN `retry_policy` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `parallel_group_path` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `activity_options` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_da40cddf645f92a0f5d3` CHECK (json_valid(`retry_policy`)), + ADD CONSTRAINT `dw_json_text_b0ad277df9011ce8dcd8` CHECK (json_valid(`parallel_group_path`)), + ADD CONSTRAINT `dw_json_text_5f011f3fc9ed1cbf3798` CHECK (json_valid(`activity_options`)); +ALTER TABLE `dw_poll_receipts` + MODIFY COLUMN `response` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + ADD CONSTRAINT `dw_json_text_b5a74d059c497aac89e1` CHECK (json_valid(`response`)); +ALTER TABLE `dw_task_completions` + MODIFY COLUMN `receipt` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + ADD CONSTRAINT `dw_json_text_49816cb4519d3d981518` CHECK (json_valid(`receipt`)); +ALTER TABLE `runtime_credentials` + MODIFY COLUMN `roles` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + MODIFY COLUMN `claims` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_075fecde0a757510fdf7` CHECK (json_valid(`roles`)), + ADD CONSTRAINT `dw_json_text_5f7fdbad00226a90f331` CHECK (json_valid(`claims`)); +ALTER TABLE `runtime_payload_completion_budgets` + MODIFY COLUMN `context` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + MODIFY COLUMN `slots` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + MODIFY COLUMN `objects` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + ADD CONSTRAINT `dw_json_text_d1bf52994d698d7d04ed` CHECK (json_valid(`context`)), + ADD CONSTRAINT `dw_json_text_4077efaae491546bebc7` CHECK (json_valid(`slots`)), + ADD CONSTRAINT `dw_json_text_c185060f2dbee09c5a6a` CHECK (json_valid(`objects`)); +ALTER TABLE `workflow_child_calls` + MODIFY COLUMN `retry_policy` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `timeout_policy` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `arguments` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `metadata` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_55a3cac14b0a3feca840` CHECK (json_valid(`retry_policy`)), + ADD CONSTRAINT `dw_json_text_40e45e4f94944c511ab5` CHECK (json_valid(`timeout_policy`)), + ADD CONSTRAINT `dw_json_text_9c078ba0f6c256bb7d9c` CHECK (json_valid(`arguments`)), + ADD CONSTRAINT `dw_json_text_016208f3b76c51671353` CHECK (json_valid(`metadata`)); +ALTER TABLE `workflow_commands` + MODIFY COLUMN `context` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_1365410084c0cd12ed37` CHECK (json_valid(`context`)); +ALTER TABLE `workflow_durable_stream_items` + MODIFY COLUMN `payload` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_9d624f24054e91d9250f` CHECK (json_valid(`payload`)); +ALTER TABLE `workflow_durable_streams` + MODIFY COLUMN `metadata` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_da0f067f234347705b1c` CHECK (json_valid(`metadata`)); +ALTER TABLE `workflow_history_events` + MODIFY COLUMN `payload` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_186a93a5a54b983f2444` CHECK (json_valid(`payload`)); +ALTER TABLE `workflow_instances` + MODIFY COLUMN `visibility_labels` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `memo` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_5dec5cdd6359edc700f2` CHECK (json_valid(`visibility_labels`)), + ADD CONSTRAINT `dw_json_text_4d686a7e3879bc8d7c6c` CHECK (json_valid(`memo`)); +ALTER TABLE `workflow_links` + MODIFY COLUMN `parallel_group_path` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_c6fec96f4176ab012436` CHECK (json_valid(`parallel_group_path`)); +ALTER TABLE `workflow_memos` + MODIFY COLUMN `value` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + MODIFY COLUMN `portable_value` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_fcdcfa03c1ad3583628c` CHECK (json_valid(`value`)), + ADD CONSTRAINT `dw_json_text_71688955589bdaf8f5c4` CHECK (json_valid(`portable_value`)); +ALTER TABLE `workflow_messages` + MODIFY COLUMN `metadata` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_b0a2ae51e86f4f80b9fb` CHECK (json_valid(`metadata`)); +ALTER TABLE `workflow_namespaces` + MODIFY COLUMN `external_payload_storage` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_4281ef3e52494643312f` CHECK (json_valid(`external_payload_storage`)); +ALTER TABLE `workflow_run_lineage_entries` + MODIFY COLUMN `payload` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_1874a82e6e4c5698bc68` CHECK (json_valid(`payload`)); +ALTER TABLE `workflow_run_summaries` + MODIFY COLUMN `visibility_labels` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_accf32ee2c1a832ab845` CHECK (json_valid(`visibility_labels`)); +ALTER TABLE `workflow_run_timeline_entries` + MODIFY COLUMN `payload` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_4a34bf0686f5464d6b56` CHECK (json_valid(`payload`)); +ALTER TABLE `workflow_run_timer_entries` + MODIFY COLUMN `payload` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_cb1a9145e9dc22308d3d` CHECK (json_valid(`payload`)); +ALTER TABLE `workflow_run_waits` + MODIFY COLUMN `payload` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_e331c14bcddf551ccfdd` CHECK (json_valid(`payload`)); +ALTER TABLE `workflow_runs` + MODIFY COLUMN `visibility_labels` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_0cabe9701a2ae7cb549e` CHECK (json_valid(`visibility_labels`)); +ALTER TABLE `workflow_schedule_history_events` + MODIFY COLUMN `payload` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_5c5faf594e8d31ebe991` CHECK (json_valid(`payload`)); +ALTER TABLE `workflow_schedules` + MODIFY COLUMN `spec` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `action` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `memo` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `search_attributes` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `visibility_labels` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `recent_actions` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `buffered_actions` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_d7aa273e112ccdb9bce4` CHECK (json_valid(`spec`)), + ADD CONSTRAINT `dw_json_text_0779e5110faa8bb3a1ce` CHECK (json_valid(`action`)), + ADD CONSTRAINT `dw_json_text_127f3a5484eae46b4296` CHECK (json_valid(`memo`)), + ADD CONSTRAINT `dw_json_text_348ceaaa72578f94b30d` CHECK (json_valid(`search_attributes`)), + ADD CONSTRAINT `dw_json_text_260b502951aabe6967a2` CHECK (json_valid(`visibility_labels`)), + ADD CONSTRAINT `dw_json_text_023daf97483a6925003e` CHECK (json_valid(`recent_actions`)), + ADD CONSTRAINT `dw_json_text_fcbf8efdd61dbed85da4` CHECK (json_valid(`buffered_actions`)); +ALTER TABLE `workflow_search_attributes` + MODIFY COLUMN `value_keyword_list` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'For type=keyword_list (ordered exact-match values)', + ADD CONSTRAINT `dw_json_text_df0541f7f01fef92919f` CHECK (json_valid(`value_keyword_list`)); +ALTER TABLE `workflow_service_calls` + MODIFY COLUMN `deadline_policy` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `idempotency_policy` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `cancellation_policy` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `retry_policy` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `boundary_policy` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `metadata` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `outcome_metadata` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `caller_principal_roles` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `caller_principal_claims` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_718533c1e82804b31504` CHECK (json_valid(`deadline_policy`)), + ADD CONSTRAINT `dw_json_text_89af2bddb6216607276e` CHECK (json_valid(`idempotency_policy`)), + ADD CONSTRAINT `dw_json_text_77a64a96c9d03d965701` CHECK (json_valid(`cancellation_policy`)), + ADD CONSTRAINT `dw_json_text_89f01ea9712aa07575bf` CHECK (json_valid(`retry_policy`)), + ADD CONSTRAINT `dw_json_text_e06e9f587b4efc8ce3e9` CHECK (json_valid(`boundary_policy`)), + ADD CONSTRAINT `dw_json_text_4a6c5e40752d4ed0bdb4` CHECK (json_valid(`metadata`)), + ADD CONSTRAINT `dw_json_text_b4ffcdd589152c314969` CHECK (json_valid(`outcome_metadata`)), + ADD CONSTRAINT `dw_json_text_5a480bd9165f2eb68e43` CHECK (json_valid(`caller_principal_roles`)), + ADD CONSTRAINT `dw_json_text_cb4fc45f46fe05d68f99` CHECK (json_valid(`caller_principal_claims`)); +ALTER TABLE `workflow_service_endpoints` + MODIFY COLUMN `boundary_policy` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `metadata` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_6e65cde38938c5bf2752` CHECK (json_valid(`boundary_policy`)), + ADD CONSTRAINT `dw_json_text_3c3c6c7139ec6ffc9fd5` CHECK (json_valid(`metadata`)); +ALTER TABLE `workflow_service_operations` + MODIFY COLUMN `handler_binding` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `deadline_policy` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `idempotency_policy` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `cancellation_policy` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `retry_policy` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `boundary_policy` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `metadata` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_e844533982a5ba5f974a` CHECK (json_valid(`handler_binding`)), + ADD CONSTRAINT `dw_json_text_aaf235f8970ff7618e0c` CHECK (json_valid(`deadline_policy`)), + ADD CONSTRAINT `dw_json_text_e9b84c12c63f3f05d4f8` CHECK (json_valid(`idempotency_policy`)), + ADD CONSTRAINT `dw_json_text_13e161b371e6bd18c390` CHECK (json_valid(`cancellation_policy`)), + ADD CONSTRAINT `dw_json_text_2409229a6279aa76321a` CHECK (json_valid(`retry_policy`)), + ADD CONSTRAINT `dw_json_text_05dd84f339d72c2ea677` CHECK (json_valid(`boundary_policy`)), + ADD CONSTRAINT `dw_json_text_818561935c015a8f258c` CHECK (json_valid(`metadata`)); +ALTER TABLE `workflow_services` + MODIFY COLUMN `boundary_policy` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `metadata` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_1af5bae7acfe0b9d3f11` CHECK (json_valid(`boundary_policy`)), + ADD CONSTRAINT `dw_json_text_4e612803d0ef9c0cfcaa` CHECK (json_valid(`metadata`)); +ALTER TABLE `workflow_signal_records` + MODIFY COLUMN `validation_errors` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_87990e1e6c171fd7edb3` CHECK (json_valid(`validation_errors`)); +ALTER TABLE `workflow_tasks` + MODIFY COLUMN `payload` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_edce43b6b7b815300bd1` CHECK (json_valid(`payload`)); +ALTER TABLE `workflow_update_validation_tasks` + MODIFY COLUMN `command_context` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `validation_errors` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_8648c83f3febc7faec99` CHECK (json_valid(`command_context`)), + ADD CONSTRAINT `dw_json_text_fc8e8804602cda328168` CHECK (json_valid(`validation_errors`)); +ALTER TABLE `workflow_updates` + MODIFY COLUMN `validation_errors` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_efe19529950144852322` CHECK (json_valid(`validation_errors`)); +ALTER TABLE `workflow_worker_build_id_rollouts` + MODIFY COLUMN `workflow_types` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_19de9b23ee283257b769` CHECK (json_valid(`workflow_types`)); +ALTER TABLE `workflow_worker_compatibility_heartbeats` + MODIFY COLUMN `supported` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + ADD CONSTRAINT `dw_json_text_c9c8573bb83f23ce864d` CHECK (json_valid(`supported`)); +ALTER TABLE `workflow_worker_registrations` + MODIFY COLUMN `supported_workflow_types` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `workflow_definition_fingerprints` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `workflow_command_contracts` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `supported_activity_types` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `capabilities` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `capability_manifest` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + MODIFY COLUMN `process_metrics` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_9e5445d614888bb3661c` CHECK (json_valid(`supported_workflow_types`)), + ADD CONSTRAINT `dw_json_text_4f49aa63cb080bc75d50` CHECK (json_valid(`workflow_definition_fingerprints`)), + ADD CONSTRAINT `dw_json_text_5ff38312fcf7d855445d` CHECK (json_valid(`workflow_command_contracts`)), + ADD CONSTRAINT `dw_json_text_4d23abbe7d6f10d67024` CHECK (json_valid(`supported_activity_types`)), + ADD CONSTRAINT `dw_json_text_5db648194e26be45f445` CHECK (json_valid(`capabilities`)), + ADD CONSTRAINT `dw_json_text_d1357dfe1fe52c7b2691` CHECK (json_valid(`capability_manifest`)), + ADD CONSTRAINT `dw_json_text_34d29bf4c212ababa706` CHECK (json_valid(`process_metrics`)); +ALTER TABLE `workflow_worker_sessions` + MODIFY COLUMN `requirements` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + ADD CONSTRAINT `dw_json_text_177d4c6e72d9b9fdebda` CHECK (json_valid(`requirements`)); diff --git a/rust/src/runtime/backend.rs b/rust/src/runtime/backend.rs index abf3dc9a8..b252481c2 100644 --- a/rust/src/runtime/backend.rs +++ b/rust/src/runtime/backend.rs @@ -793,8 +793,9 @@ impl Backend for MySql { let mut tx = pool.begin().await?; // InnoDB row locking releases on commit/rollback/process loss. This // first slice serializes transitions on its single ownership row. - sqlx::query("SELECT engine FROM dw_server_schema WHERE engine='rust-development' AND version=? FOR UPDATE") - .bind(mysql::VERSION).fetch_one(&mut *tx).await?; + // MariaDB stays at 12 while MySQL moved to 13 (server#1016). + sqlx::query("SELECT engine FROM dw_server_schema WHERE engine='rust-development' AND version=IF(VERSION() LIKE '%MariaDB%',?,?) FOR UPDATE") + .bind(mysql::MARIADB_VERSION).bind(mysql::MYSQL_VERSION).fetch_one(&mut *tx).await?; Ok(tx) } async fn history_ready(pool: &Pool) -> bool { diff --git a/rust/src/runtime/mysql.rs b/rust/src/runtime/mysql.rs index 720a03fdf..1d1551a8d 100644 --- a/rust/src/runtime/mysql.rs +++ b/rust/src/runtime/mysql.rs @@ -9,7 +9,11 @@ use sqlx::{ }; use std::{collections::BTreeMap, sync::OnceLock, time::Duration}; -pub const VERSION: i64 = 12; +/// The native schema version on MySQL. Version 13 stores JSON documents as +/// exact text (server#1016); MariaDB already stores them as text and stays at +/// 12. `Flavor::version` selects the one a server needs. +pub const MYSQL_VERSION: i64 = 13; +pub const MARIADB_VERSION: i64 = 12; type Catalog = Vec<(String, String)>; pub const MARKER_SQL: &str = "CREATE TABLE dw_server_schema (engine VARCHAR(64) NOT NULL PRIMARY KEY,version BIGINT NOT NULL,bootstrap_checksum VARBINARY(48) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"; @@ -60,7 +64,9 @@ impl Flavor { "\n", include_str!("../../migrations/mysql/0011_scoped_dispatch_index.sql"), "\n", - include_str!("../../migrations/mysql/0012_run_command_contracts.sql") + include_str!("../../migrations/mysql/0012_run_command_contracts.sql"), + "\n", + include_str!("../../migrations/mysql/0013_json_text.sql") ), ), Self::MariaDb => ( @@ -89,11 +95,15 @@ impl Flavor { ), }; slot.get_or_init(|| { + let description = match self { + Self::MySql => "full frozen PHP schema, native worker incarnations, session holders, stream reclaim deadlines, service call deadlines, service admission counters and committed external payload deletions and task ready origins and poll expiry indexes and scoped dispatch index and bound run command contracts and exact JSON text documents", + Self::MariaDb => "full frozen PHP schema, native worker incarnations, session holders, stream reclaim deadlines, service call deadlines, service admission counters and committed external payload deletions and task ready origins and poll expiry indexes and scoped dispatch index and bound run command contracts", + }; let mut migrations = Migrator::with_migrations(vec![Migration::new( - VERSION, - "full frozen PHP schema, native worker incarnations, session holders, stream reclaim deadlines, service call deadlines, service admission counters and committed external payload deletions and task ready origins and poll expiry indexes and scoped dispatch index and bound run command contracts".into(), + self.version(), + description.into(), MigrationType::Simple, - AssertSqlSafe(bootstrap_sql(sql)).into_sql_str(), + AssertSqlSafe(self.bootstrap_sql(sql)).into_sql_str(), false, )]); // A bounded named lock on this dedicated initialization connection @@ -102,9 +112,18 @@ impl Flavor { migrations }) } + /// The schema-12 catalog: MySQL's retained one, MariaDB's current one. + /// Steps before MySQL's JSON text step build toward it; a resumed upgrade + /// may also find a table already past it, in its current definition. + fn v12_catalog(self) -> Result { + match self { + Self::MySql => Ok(serde_json::from_str(&canonical(self.earlier(12).expect("retained v12 schema").1))?), + Self::MariaDb => self.expected(), + } + } fn expected(self) -> Result { Ok(serde_json::from_str(&canonical(match self { - Self::MySql => include_str!("../../migrations/mysql/0012_catalog.json"), + Self::MySql => include_str!("../../migrations/mysql/0013_catalog.json"), Self::MariaDb => include_str!("../../migrations/mariadb/0012_catalog.json"), }))?) } @@ -172,6 +191,12 @@ impl Flavor { "CREATE TABLE IF NOT EXISTS dw_service_admission_counters", )) } + pub(super) fn version(self) -> i64 { + match self { + Self::MySql => MYSQL_VERSION, + Self::MariaDb => MARIADB_VERSION, + } + } pub(super) fn name(self) -> &'static str { match self { Self::MySql => "mysql", @@ -190,6 +215,7 @@ impl Flavor { static MYSQL_9: OnceLock = OnceLock::new(); static MARIA_9: OnceLock = OnceLock::new(); static MYSQL_11: OnceLock = OnceLock::new(); + static MYSQL_12: OnceLock = OnceLock::new(); static MARIA_11: OnceLock = OnceLock::new(); let description = "full frozen PHP schema, native worker incarnations, session holders, stream reclaim deadlines, service call deadlines and service admission counters"; let earlier = |slot: &'static OnceLock, version, description: &'static str, sql: &'static str| { @@ -198,6 +224,36 @@ impl Flavor { }) }; match (self, version) { + (Self::MySql, 12) => Some(( + MYSQL_12.get_or_init(|| Migration::new( + 12, + "full frozen PHP schema, native worker incarnations, session holders, stream reclaim deadlines, service call deadlines, service admission counters and committed external payload deletions and task ready origins and poll expiry indexes and scoped dispatch index and bound run command contracts".into(), + MigrationType::Simple, + AssertSqlSafe(bootstrap_sql_v11(concat!( + include_str!("../../migrations/mysql/0003_full_schema.sql"), + "\n", + include_str!("../../migrations/mysql/0004_worker_incarnations.sql"), + "\n", + include_str!("../../migrations/mysql/0005_worker_session_holders.sql"), + "\n", + include_str!("../../migrations/mysql/0006_stream_reclaims.sql"), + "\n", + include_str!("../../migrations/mysql/0007_service_calls.sql"), + "\n", + include_str!("../../migrations/mysql/0008_external_payload_deletions.sql"), + "\n", + include_str!("../../migrations/mysql/0009_task_ready_origin.sql"), + "\n", + include_str!("../../migrations/mysql/0010_poll_expiry_indexes.sql"), + "\n", + include_str!("../../migrations/mysql/0011_scoped_dispatch_index.sql"), + "\n", + include_str!("../../migrations/mysql/0012_run_command_contracts.sql") + ))).into_sql_str(), + false, + )), + include_str!("../../migrations/mysql/0012_catalog.json"), + )), (Self::MySql, 11) => Some(( MYSQL_11.get_or_init(|| Migration::new( 11, @@ -404,6 +460,7 @@ impl Flavor { (Self::MariaDb, 10) => Some(include_str!("../../migrations/mariadb/0011_scoped_dispatch_index.sql")), (Self::MySql, 11) => Some(include_str!("../../migrations/mysql/0012_run_command_contracts.sql")), (Self::MariaDb, 11) => Some(include_str!("../../migrations/mariadb/0012_run_command_contracts.sql")), + (Self::MySql, 12) => Some(include_str!("../../migrations/mysql/0013_json_text.sql")), _ => None, } } @@ -449,10 +506,84 @@ fn bootstrap_sql_v11(sql: &str) -> String { sql.replace("CREATE INDEX dw_workflow_tasks_scoped_dispatch ON workflow_tasks (namespace,queue,task_type(16),status(16),priority,available_at,id);", "") } -/// Version 12 adds only a native table, created like every other native -/// table of the cumulative bootstrap. -fn bootstrap_sql(sql: &str) -> String { - bootstrap_sql_v11(sql) +impl Flavor { + /// Version 12 adds only a native table, created like every other native + /// table of the cumulative bootstrap. MySQL's version 13 creates each JSON + /// document column as exact text directly, as its upgrade ALTERs it. + fn bootstrap_sql(self, sql: &str) -> String { + match self { + Self::MySql => json_text_bootstrap_sql(&bootstrap_sql_v11(sql)), + Self::MariaDb => bootstrap_sql_v11(sql), + } + } +} + +/// The exact text definition and enforced check for each MySQL `json` +/// column (server#1016). The constraint name is `dw_json_text_` and the first +/// 20 hex digits of SHA-256 over table, NUL and column. +fn json_text_check_name(table: &str, column: &str) -> String { + use sha2::{Digest, Sha256}; + let digest = Sha256::digest(format!("{table}\0{column}").as_bytes()); + let hex: String = digest.iter().map(|byte| format!("{byte:02x}")).collect(); + format!("dw_json_text_{}", &hex[..20]) +} + +/// Rewrites every compiled `json` column of the MySQL bootstrap to the +/// version-13 text definition with its check, and drops the upgrade ALTERs, +/// so a fresh or resumed initialization creates final tables directly. +fn json_text_bootstrap_sql(sql: &str) -> String { + let column = regex_lite::Regex::new(r"(?i)^(\s*)`?(\w+)`? json( NOT NULL| DEFAULT NULL)?( COMMENT '[^']*')?(,?)\s*$") + .expect("json column pattern"); + let table = regex_lite::Regex::new(r"^\s*CREATE TABLE (?:IF NOT EXISTS )?`?(\w+)`?").expect("table pattern"); + let sql = sql.replace(include_str!("../../migrations/mysql/0013_json_text.sql"), ""); + let mut out: Vec = Vec::new(); + let (mut current, mut checks) = (String::new(), Vec::new()); + for line in sql.split('\n') { + if let Some(found) = table.captures(line) { + current = found[1].to_owned(); + checks.clear(); + } + if let Some(found) = column.captures(line) { + let name = found[2].to_owned(); + checks.push(format!(" CONSTRAINT `{}` CHECK (json_valid(`{name}`))", json_text_check_name(¤t, &name))); + out.push(format!("{}`{name}` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin{}{}{}", &found[1], + found.get(3).map_or("", |m| m.as_str()), found.get(4).map_or("", |m| m.as_str()), &found[5])); + continue; + } + if line.starts_with(")") && line.contains("ENGINE") && !checks.is_empty() { + let last = out.last_mut().expect("a definition before the table footer"); + last.push(','); + last.push('\n'); + last.push_str(&checks.join(",\n")); + checks.clear(); + } + out.push(line.to_owned()); + } + out.join("\n") +} + +/// Version 13 on MySQL: each JSON document column becomes exact text, one +/// atomic ALTER per table. A resumed upgrade or takeover accepts each table +/// only in its exact version-12 or version-13 definition. +pub(super) async fn ensure_json_text(connection: &mut MySqlConnection, flavor: Flavor) -> Result<()> { + let Flavor::MySql = flavor else { return Ok(()) }; + let (_, old) = flavor.earlier(12).expect("retained v12 schema"); + let old: BTreeMap = serde_json::from_str::(&canonical(old))?.into_iter().collect(); + let expected: BTreeMap = flavor.expected()?.into_iter().collect(); + let actual: BTreeMap = catalog(connection).await?.into_iter().collect(); + for statement in flavor.step(12).expect("compiled json text step").split(";\n").map(str::trim) { + let Some(rest) = statement.lines().find(|line| line.starts_with("ALTER TABLE `")) else { continue }; + let name = rest.trim_start_matches("ALTER TABLE `").trim_end_matches('`').to_owned(); + match actual.get(&name) { + found if found == expected.get(&name) => continue, + found if found.is_some() && found == old.get(&name) => { + let alter = statement[statement.find("ALTER TABLE").expect("alter")..].to_owned(); + connection.execute(AssertSqlSafe(alter)).await?; + } + _ => return Err(refuse(StatusCode::CONFLICT, "native_schema_catalog_mismatch")), + } + } + Ok(()) } /// Creating the bound-contract table commits on its own. A resumed upgrade @@ -461,7 +592,7 @@ pub(super) async fn ensure_run_command_contracts(connection: &mut MySqlConnectio let sql = flavor.step(11).expect("compiled run command contracts"); connection.execute(AssertSqlSafe(sql.replace("CREATE TABLE ", "CREATE TABLE IF NOT EXISTS "))).await?; let table = |rows: Catalog| rows.into_iter().find(|(name, _)| name == "dw_run_command_contracts"); - if table(catalog(connection).await?) != table(flavor.expected()?) { + if table(catalog(connection).await?) != table(flavor.v12_catalog()?) { return Err(refuse(StatusCode::CONFLICT, "native_schema_catalog_mismatch")); } Ok(()) @@ -474,7 +605,8 @@ pub(super) async fn ensure_ready_origin(connection: &mut MySqlConnection, flavor let old: Catalog = serde_json::from_str(&canonical(old))?; let actual = catalog(connection).await?; let task = |rows: Catalog| rows.into_iter().find(|(name, _)| name == "workflow_tasks"); - if task(actual.clone()) == task(flavor.expected()?) { return Ok(()); } + let found = task(actual.clone()); + if found == task(flavor.v12_catalog()?) || found == task(flavor.expected()?) { return Ok(()); } let (_, ready) = flavor.earlier(10).expect("retained v10 schema"); let expected = task(serde_json::from_str(&canonical(ready))?); match task(actual) { @@ -495,14 +627,15 @@ pub(super) async fn ensure_ready_origin(connection: &mut MySqlConnection, flavor pub(super) async fn ensure_poll_expiry_indexes(connection: &mut MySqlConnection, flavor: Flavor) -> Result<()> { let (_, old) = flavor.earlier(9).expect("retained v9 schema"); let old: Catalog = serde_json::from_str(&canonical(old))?; - let expected = flavor.expected()?; + let expected = flavor.v12_catalog()?; + let current = flavor.expected()?; for (table, sql) in [ ("dw_query_cache", "CREATE INDEX dw_query_cache_expiration ON dw_query_cache (expiration)"), ("dw_poll_receipts", "CREATE INDEX dw_poll_receipts_expiration ON dw_poll_receipts (expires_at)"), ] { let found = catalog(connection).await?; let row = |rows: &Catalog| rows.iter().find(|(name, _)| name == table).cloned(); - if row(&found) == row(&expected) { continue; } + if row(&found) == row(&expected) || row(&found) == row(¤t) { continue; } if row(&found) != row(&old) { return Err(refuse(StatusCode::CONFLICT, "native_schema_catalog_mismatch")); } connection.execute(sql).await?; if row(&catalog(connection).await?) != row(&expected) { @@ -518,9 +651,9 @@ pub(super) async fn ensure_scoped_dispatch_index(connection: &mut MySqlConnectio let (_, old) = flavor.earlier(10).expect("retained v10 schema"); let old: Catalog = serde_json::from_str(&canonical(old))?; let task = |rows: Catalog| rows.into_iter().find(|(name, _)| name == "workflow_tasks"); - let expected = task(flavor.expected()?); + let expected = task(flavor.v12_catalog()?); let found = task(catalog(connection).await?); - if found == expected { return Ok(()); } + if found == expected || found == task(flavor.expected()?) { return Ok(()); } if found != task(old) { return Err(refuse(StatusCode::CONFLICT, "native_schema_catalog_mismatch")); } connection.execute(flavor.step(10).expect("compiled scoped dispatch index")).await?; if task(catalog(connection).await?) != expected { @@ -774,12 +907,12 @@ async fn history( }; if markers.len() != 1 || markers[0].0 != engine - || markers[0].1 != VERSION + || markers[0].1 != flavor.version() || markers[0].2 != flavor.checksum() { return Err(match markers.as_slice() { - [(found, version, _)] if !initializing && (found != engine || *version != VERSION) => { - super::schema_upgrade::marker_refusal(found, *version, VERSION) + [(found, version, _)] if !initializing && (found != engine || *version != flavor.version()) => { + super::schema_upgrade::marker_refusal(found, *version, flavor.version()) } _ => refuse(StatusCode::CONFLICT, "unsupported_rust_schema"), }); @@ -802,7 +935,7 @@ async fn history( return Ok(None); } if applied.len() != 1 - || applied[0].0 != VERSION + || applied[0].0 != flavor.version() || applied[0].2 != flavor.checksum() || (!initializing && !applied[0].1) { @@ -904,7 +1037,7 @@ async fn preflight(connection: &mut MySqlConnection, flavor: Flavor) -> Result<( /// `rust-upgrading`, which fences every node, and a rerun resumes from /// whatever the interrupted run left. Returns the backend and the version /// found; a current database is only verified. -pub(super) async fn upgrade(connect_options: MySqlConnectOptions) -> Result<(&'static str, i64)> { +pub(super) async fn upgrade(connect_options: MySqlConnectOptions) -> Result<(&'static str, i64, i64)> { use super::schema_upgrade::{DEVELOPMENT_ENGINE, UPGRADING_ENGINE, marker_refusal}; let mut connection = MySqlConnection::connect_with(&options(connect_options)).await?; let result = async { @@ -927,15 +1060,15 @@ pub(super) async fn upgrade(connect_options: MySqlConnectOptions) -> Result<(&'s return Err(refuse(StatusCode::CONFLICT, "unsupported_rust_schema")); }; let from = *from; - if engine == DEVELOPMENT_ENGINE && from == VERSION { + if engine == DEVELOPMENT_ENGINE && from == flavor.version() { verify(&mut connection, flavor).await?; - return Ok((flavor.name(), from)); + return Ok((flavor.name(), from, flavor.version())); } let Some((earlier, earlier_catalog)) = flavor .earlier(from) .filter(|_| engine == DEVELOPMENT_ENGINE || engine == UPGRADING_ENGINE) else { - return Err(marker_refusal(engine, from, VERSION)); + return Err(marker_refusal(engine, from, flavor.version())); }; if checksum.as_slice() != earlier.checksum.as_ref() { return Err(refuse(StatusCode::CONFLICT, "unsupported_rust_schema")); @@ -968,12 +1101,15 @@ pub(super) async fn upgrade(connect_options: MySqlConnectOptions) -> Result<(&'s // new definition, and every earlier relation is still there. let earlier_tables: BTreeMap<_, _> = earlier_catalog.into_iter().collect(); let current: BTreeMap<_, _> = flavor.expected()?.into_iter().collect(); + // On MySQL a table may also stand at its schema-12 definition, + // before the JSON text step reached it. + let v12: BTreeMap<_, _> = flavor.v12_catalog()?.into_iter().collect(); // The ready-origin step can commit before the scoped index step. // Accept that exact retained intermediate task definition too. let intermediate: Catalog = serde_json::from_str(&canonical(flavor.earlier(10).expect("retained v10 schema").1))?; let intermediate_task = intermediate.into_iter().find(|(name, _)| name == "workflow_tasks"); let partial = actual.iter().all(|(name, ddl)| { - earlier_tables.get(name) == Some(ddl) || current.get(name) == Some(ddl) + earlier_tables.get(name) == Some(ddl) || current.get(name) == Some(ddl) || v12.get(name) == Some(ddl) || name == "workflow_tasks" && intermediate_task.as_ref().is_some_and(|(_, task)| task == ddl) }) && earlier_tables .keys() @@ -984,7 +1120,11 @@ pub(super) async fn upgrade(connect_options: MySqlConnectOptions) -> Result<(&'s } // Each step's own marker update names the development engine, so it // changes nothing while the marker fences the upgrade. - for version in from..VERSION { + for version in from..flavor.version() { + if version == 12 { + ensure_json_text(&mut connection, flavor).await?; + continue; + } if version == 8 { ensure_ready_origin(&mut connection, flavor).await?; continue; @@ -1025,7 +1165,7 @@ pub(super) async fn upgrade(connect_options: MySqlConnectOptions) -> Result<(&'s .await?; let marked = sqlx::query("UPDATE dw_server_schema SET engine=?,version=?,bootstrap_checksum=? WHERE engine=? AND version=? AND bootstrap_checksum=?") .bind(DEVELOPMENT_ENGINE) - .bind(VERSION) + .bind(flavor.version()) .bind(flavor.checksum()) .bind(UPGRADING_ENGINE) .bind(from) @@ -1037,7 +1177,7 @@ pub(super) async fn upgrade(connect_options: MySqlConnectOptions) -> Result<(&'s } tx.commit().await?; verify(&mut connection, flavor).await?; - Ok::<_, super::RuntimeError>((flavor.name(), from)) + Ok::<_, super::RuntimeError>((flavor.name(), from, flavor.version())) } .await; // Named locks are session scoped, including on errors/process death. @@ -1110,7 +1250,7 @@ impl MySqlStorage { // when nothing else exists yet. let marked:i64=sqlx::query_scalar("SELECT COUNT(*) FROM dw_server_schema").fetch_one(&mut connection).await?; if marked==0 { - sqlx::query("INSERT INTO dw_server_schema(engine,version,bootstrap_checksum) VALUES ('rust-initializing',?,?)").bind(VERSION).bind(flavor.checksum()).execute(&mut connection).await?; + sqlx::query("INSERT INTO dw_server_schema(engine,version,bootstrap_checksum) VALUES ('rust-initializing',?,?)").bind(flavor.version()).bind(flavor.checksum()).execute(&mut connection).await?; } let engine:String=sqlx::query_scalar("SELECT engine FROM dw_server_schema").fetch_one(&mut connection).await?; if engine=="rust-initializing" { @@ -1122,11 +1262,11 @@ impl MySqlStorage { // checksum and absence of application data were verified. let migration=flavor.migrator().iter().next().unwrap(); connection.execute(migration.sql.clone()).await?; - sqlx::query("UPDATE _sqlx_migrations SET success=TRUE WHERE version=? AND checksum=? AND success=FALSE").bind(VERSION).bind(flavor.checksum()).execute(&mut connection).await?; + sqlx::query("UPDATE _sqlx_migrations SET success=TRUE WHERE version=? AND checksum=? AND success=FALSE").bind(flavor.version()).bind(flavor.checksum()).execute(&mut connection).await?; } else {flavor.migrator().run(&mut connection).await?;} if catalog(&mut connection).await?!=flavor.expected()? {return Err(refuse(StatusCode::CONFLICT,"native_schema_catalog_mismatch"));} let mut tx=connection.begin().await?; - sqlx::query("UPDATE dw_server_schema SET engine='rust-development' WHERE engine='rust-initializing' AND version=? AND bootstrap_checksum=?").bind(VERSION).bind(flavor.checksum()).execute(&mut *tx).await?; + sqlx::query("UPDATE dw_server_schema SET engine='rust-development' WHERE engine='rust-initializing' AND version=? AND bootstrap_checksum=?").bind(flavor.version()).bind(flavor.checksum()).execute(&mut *tx).await?; tx.commit().await?; } verify(&mut connection,flavor).await?; @@ -1233,7 +1373,7 @@ mod tests { async fn initializing(connection: &mut MySqlConnection, flavor: Flavor) { connection.execute(MARKER_SQL).await.unwrap(); sqlx::query("INSERT INTO dw_server_schema VALUES ('rust-initializing',?,?)") - .bind(VERSION) + .bind(flavor.version()) .bind(flavor.checksum()) .execute(connection) .await @@ -1258,7 +1398,7 @@ mod tests { } async fn dirty(connection: &mut MySqlConnection, flavor: Flavor) { connection.execute(LEDGER_SQL).await.unwrap(); - sqlx::query("INSERT INTO _sqlx_migrations VALUES (?, 'interrupted qualification', CURRENT_TIMESTAMP, FALSE, ?, -1)").bind(VERSION).bind(flavor.checksum()).execute(connection).await.unwrap(); + sqlx::query("INSERT INTO _sqlx_migrations VALUES (?, 'interrupted qualification', CURRENT_TIMESTAMP, FALSE, ?, -1)").bind(flavor.version()).bind(flavor.checksum()).execute(connection).await.unwrap(); } #[test] @@ -2535,6 +2675,32 @@ mod tests { migration.checksum.iter().map(|byte| format!("{byte:02x}")).collect() } + #[test] + fn mysql_version_13_moves_json_to_text_and_mariadb_stays_at_12() { + // MySQL's retained version 12 is byte for byte the released one; its + // current bootstrap creates exact text columns with enforced checks. + assert_eq!(checksum_hex(Flavor::MySql.earlier(12).unwrap().0), + include_str!("../../migrations/mysql/0012_checksum.txt").trim()); + assert_ne!(Flavor::MySql.checksum(), Flavor::MySql.earlier(12).unwrap().0.checksum.as_ref()); + // MariaDB stores JSON as text already: its bootstrap is unchanged. + assert_eq!(Flavor::MariaDb.version(), 12); + assert!(Flavor::MariaDb.earlier(12).is_none()); + assert_eq!(checksum_hex(Flavor::MariaDb.migration()), + include_str!("../../migrations/mariadb/0012_checksum.txt").trim()); + let sql: &str = Flavor::MySql.migration().sql.as_ref(); + assert_eq!(Flavor::MySql.version(), 13); + assert!(!sql.contains("ALTER TABLE `activity_executions`"), "upgrade ALTERs are folded into the bootstrap"); + assert!(!regex_lite::Regex::new(r"(?i)`?\w+`? json( |,)").unwrap().is_match(sql), "no json column remains"); + assert_eq!(sql.matches("CHECK (json_valid(").count(), 75); + assert_eq!(sql.matches("longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin").count(), 75); + let step = Flavor::MySql.step(12).unwrap(); + assert_eq!(step.matches("ALTER TABLE `").count(), 36); + assert_eq!(step.matches("ADD CONSTRAINT `dw_json_text_").count(), 75); + assert_eq!(json_text_check_name("activity_executions", "retry_policy"), "dw_json_text_da40cddf645f92a0f5d3"); + let expected: Catalog = serde_json::from_str(&canonical(include_str!("../../migrations/mysql/0013_catalog.json"))).unwrap(); + assert_eq!(expected.iter().map(|(_, ddl)| ddl.matches("CHECK (json_valid(").count()).sum::(), 75); + } + #[test] fn version_8_checksums_and_resumable_current_bootstrap_are_distinct() { for (flavor, pinned) in [ @@ -2685,7 +2851,7 @@ mod tests { flavor } - fn upgrade_refused(result: Result<(&'static str, i64)>, expected: &str) { + fn upgrade_refused(result: Result<(&'static str, i64, i64)>, expected: &str) { match result { Err(super::super::RuntimeError::Refused { reason, .. }) => assert_eq!(reason, expected), Err(error) => panic!("unexpected upgrade error: {error}"), @@ -2733,7 +2899,7 @@ mod tests { connection.execute(flavor.step(11).unwrap()).await.unwrap(); connection.close().await.unwrap(); } - assert_eq!(upgrade(db.options.clone()).await.unwrap(), (flavor.name(), 11)); + assert_eq!(upgrade(db.options.clone()).await.unwrap(), (flavor.name(), 11, flavor.version())); MySqlStorage::check(db.options.clone()).await.unwrap(); let mut connection = db.connection().await; let kept: String = sqlx::query_scalar("SELECT description FROM workflow_namespaces WHERE name='kept'").fetch_one(&mut connection).await.unwrap(); @@ -2746,6 +2912,113 @@ mod tests { } } + #[tokio::test] + #[ignore = "requires the existing disposable mysql source suite"] + async fn qualification_version_12_json_text_upgrade_resumes_and_keeps_rows() { + for stage in ["before", "partial", "foreign"] { + let db = Database::new().await; + let mut connection = db.connection().await; + let flavor = Flavor::detect(&mut connection).await.unwrap(); + connection.close().await.unwrap(); + let Flavor::MySql = flavor else { + // MariaDB stores JSON as text already and stays at 12. + assert_eq!(flavor.version(), 12); + db.remove().await; + return; + }; + retained_version_database(&db, 12).await; + let mut connection = db.connection().await; + // A row written at 12 holds MySQL's own double; it keeps it. + connection.execute(r#"INSERT INTO workflow_history_events(id,workflow_run_id,sequence,event_type,payload) VALUES ('01HISTORYROW0000000000000A','01RUN0000000000000000000AA',1,'WorkflowStarted','{"x":1.4820142857142857,"y":"λ"}')"#).await.unwrap(); + let before: String = sqlx::query_scalar("SELECT CAST(payload AS CHAR) FROM workflow_history_events").fetch_one(&mut connection).await.unwrap(); + assert_eq!(before, r#"{"x": 1.4820142857142855, "y": "λ"}"#); + match stage { + "before" => {} + "partial" => { + // Two table rebuilds committed before the run was interrupted. + connection.execute("UPDATE dw_server_schema SET engine='rust-upgrading'").await.unwrap(); + for statement in flavor.step(12).unwrap().split(";\n").filter(|s| s.contains("ALTER TABLE")).take(2) { + let alter = &statement[statement.find("ALTER TABLE").unwrap()..]; + connection.execute(AssertSqlSafe(alter.to_owned())).await.unwrap(); + } + } + _ => { + // A table that is neither the 12 nor the 13 definition. + connection.execute("UPDATE dw_server_schema SET engine='rust-upgrading'").await.unwrap(); + connection.execute("ALTER TABLE workflow_history_events MODIFY COLUMN payload LONGTEXT").await.unwrap(); + } + } + connection.close().await.unwrap(); + if stage == "before" { + refused(MySqlStorage::open(db.options.clone()).await, "native_schema_upgrade_required"); + } + if stage == "foreign" { + upgrade_refused(upgrade(db.options.clone()).await, "native_schema_catalog_mismatch"); + db.remove().await; + continue; + } + assert_eq!(upgrade(db.options.clone()).await.unwrap(), (flavor.name(), 12, 13)); + MySqlStorage::check(db.options.clone()).await.unwrap(); + let mut connection = db.connection().await; + assert_eq!(catalog(&mut connection).await.unwrap(), flavor.expected().unwrap()); + let after: String = sqlx::query_scalar("SELECT payload FROM workflow_history_events").fetch_one(&mut connection).await.unwrap(); + assert_eq!(after, before, "the upgrade keeps each stored document as MySQL rendered it"); + let enforced: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA=DATABASE() AND CONSTRAINT_TYPE='CHECK' AND ENFORCED='YES' AND CONSTRAINT_NAME LIKE 'dw\\_json\\_text\\_%'") + .fetch_one(&mut connection).await.unwrap(); + assert_eq!(enforced, 75); + // New text keeps every digit, and invalid JSON is refused. + connection.execute(r#"UPDATE workflow_history_events SET payload='{"x":1.4820142857142857}'"#).await.unwrap(); + let exact: String = sqlx::query_scalar("SELECT payload FROM workflow_history_events").fetch_one(&mut connection).await.unwrap(); + assert_eq!(exact, r#"{"x":1.4820142857142857}"#); + assert!(connection.execute("UPDATE workflow_history_events SET payload='{not json'").await.is_err()); + connection.close().await.unwrap(); + db.remove().await; + } + } + + /// Server #1016 gate: seeds `DW_JSON_TEXT_TIMING_ROWS` history rows of + /// about 1 KiB at schema 12, then times the upgrade to 13. Skipped unless + /// the variable is set. + #[tokio::test] + #[ignore = "requires the existing disposable mysql source suite"] + async fn qualification_json_text_upgrade_timing() { + let Ok(rows) = std::env::var("DW_JSON_TEXT_TIMING_ROWS") else { return }; + let rows: i64 = rows.parse().unwrap(); + let db = Database::new().await; + let mut connection = db.connection().await; + let flavor = Flavor::detect(&mut connection).await.unwrap(); + connection.close().await.unwrap(); + let Flavor::MySql = flavor else { db.remove().await; return }; + retained_version_database(&db, 12).await; + let mut connection = db.connection().await; + // A seeding helper, dropped before the upgrade so the catalog is exact. + connection.execute("CREATE TABLE timing_digits (d INT PRIMARY KEY)").await.unwrap(); + connection.execute("INSERT INTO timing_digits VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)").await.unwrap(); + let payload = r#"{"activity_execution_id":"01JZ8Y4K3Q8V6B2N1M0C9X7Z5A","activity_type":"billing.charge-card","sequence":7,"result":{"codec":"avro","blob":"BASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOADBASE64PAYLOAD"},"payload_codec":"avro","attempt_number":1,"activity_attempt_id":"01JZ8Y4K3Q8V6B2N1M0C9X7Z5B","metrics":{"duration_ms":1234.5678901234567,"ratio":0.7071067811865476,"amount":124435.71428571429},"task":{"id":"01JZ8Y4K3Q8V6B2N1M0C9X7Z5C","queue":"default","connection":"default"}}"#; + let seeded = std::time::Instant::now(); + let mut done = 0; + while done < rows { + let batch = (rows - done).min(100_000); + sqlx::query("INSERT INTO workflow_history_events(id,workflow_run_id,sequence,event_type,payload,recorded_at,recorded_at_utc) SELECT LPAD(?+n,26,'0'),LPAD(FLOOR((?+n)/50),26,'0'),MOD(?+n,50)+1,'ActivityCompleted',?,NOW(6),UTC_TIMESTAMP(6) FROM (SELECT a.d+10*b.d+100*c.d+1000*e.d+10000*f.d AS n FROM timing_digits a,timing_digits b,timing_digits c,timing_digits e,timing_digits f) g WHERE n RuntimeError { @@ -131,8 +131,8 @@ pub async fn upgrade_postgres(options: PgConnectOptions) -> Result { /// Upgrade a native MySQL or MariaDB database under the bootstrap named /// lock. An interrupted upgrade resumes when the command is run again. pub async fn upgrade_mysql(options: MySqlConnectOptions) -> Result { - let (backend, from) = super::mysql::upgrade(options).await?; - Ok(report(backend, from, super::mysql::VERSION)) + let (backend, from, to) = super::mysql::upgrade(options).await?; + Ok(report(backend, from, to)) } #[cfg(test)] diff --git a/rust/src/runtime/upgrade/takeover/mysql_journal.rs b/rust/src/runtime/upgrade/takeover/mysql_journal.rs index 9105c7718..3567b0047 100644 --- a/rust/src/runtime/upgrade/takeover/mysql_journal.rs +++ b/rust/src/runtime/upgrade/takeover/mysql_journal.rs @@ -459,6 +459,7 @@ async fn finish(connection: &mut MySqlConnection, run: &Run<'_>) -> Result<()> { mysql::ensure_ready_origin(connection, run.flavor).await?; mysql::ensure_scoped_dispatch_index(connection, run.flavor).await?; mysql::ensure_run_command_contracts(connection, run.flavor).await?; + mysql::ensure_json_text(connection, run.flavor).await?; connection.execute(LEDGER_SQL).await?; let migration = run.flavor.migration(); let mut tx = connection.begin().await?; @@ -469,7 +470,7 @@ async fn finish(connection: &mut MySqlConnection, run: &Run<'_>) -> Result<()> { .execute(&mut *tx) .await?; let marked = sqlx::query("UPDATE dw_server_schema SET engine='rust-development',version=?,bootstrap_checksum=? WHERE engine=? AND version=?") - .bind(mysql::VERSION) + .bind(run.flavor.version()) .bind(run.flavor.checksum()) .bind(format!("{TAKEOVER_ENGINE}{}", run.layout)) .bind(LAST) diff --git a/rust/tests/execution.rs b/rust/tests/execution.rs index 7acbf8c9b..96938e758 100644 --- a/rust/tests/execution.rs +++ b/rust/tests/execution.rs @@ -212,6 +212,8 @@ mod command_batches; mod child_open_parent_task; #[path = "execution/update_routing.rs"] mod update_routing; +#[path = "execution/json_doubles.rs"] +mod json_doubles; #[path = "execution/update_reads.rs"] mod update_reads; #[path = "execution/command_payloads.rs"] diff --git a/rust/tests/execution/json_doubles.rs b/rust/tests/execution/json_doubles.rs new file mode 100644 index 000000000..ad88f96ea --- /dev/null +++ b/rust/tests/execution/json_doubles.rs @@ -0,0 +1,74 @@ +use super::*; + +// server#1016: JSON doubles come back bit-exact on every backend, MySQL +// included, through the run's memo, its double search attributes (describe, +// list and WorkflowStarted) and its activity heartbeat details. MySQL's json +// type used to store its own binary double, one ULP off for some values; +// schema 13 stores each document as exact utf8mb4_bin text. +const DOUBLES: [&str; 6] = ["1.4820142857142857", "124435.71428571429", "33.333333333333336", + "0.7071067811865476", "1.0000000000000002", "2.2250738585072014e-308"]; + +fn exact(found: &Value, key: &str, text: &str, surface: &str) { + let wanted: f64 = text.parse().unwrap(); + let actual = found[key].as_f64().unwrap_or_else(|| panic!("{surface} {key} missing: {found}")); + assert_eq!(actual.to_bits(), wanted.to_bits(), "{surface} {key}: {actual} is not {text}"); +} + +#[tokio::test] +async fn doubles_come_back_bit_exact_through_history_memo_search_attributes_and_heartbeats() { + let database = TestDatabase::new().await; + let runtime = database.open().await.unwrap(); + let app = router(runtime.clone()); + if matches!(database, TestDatabase::MySql { .. }) { + let types = text_rows(&database, "", "", "SELECT COLUMN_NAME, DATA_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='workflow_history_events' AND COLUMN_NAME='payload'").await; + assert_eq!(types, [("payload".to_owned(), "longtext".to_owned())]); + } + let mut memo = serde_json::Map::new(); + let mut attributes = serde_json::Map::new(); + for (index, text) in DOUBLES.iter().enumerate() { + let value: f64 = text.parse().unwrap(); + memo.insert(format!("d{index}"), json!(value)); + let name = format!("Dbl{index}"); + let (status, body) = request(&app, "POST", "/api/search-attributes", json!({"name":name,"type":"double"})).await; + assert_eq!(status, StatusCode::CREATED, "{body}"); + attributes.insert(name, json!(value)); + } + let (status, body) = request(&app, "POST", "/api/worker/register", json!({"worker_id":"doubles-worker","task_queue":"test", + "runtime":"php","supported_workflow_types":["echo"],"supported_activity_types":["measure"]})).await; + assert_eq!(status, StatusCode::CREATED, "{body}"); + let (status, started) = request(&app, "POST", "/api/workflows", json!({"workflow_id":"doubles","workflow_type":"echo", + "task_queue":"test","input":envelope(Payload::Array(vec![])),"memo":{"doubles":memo},"search_attributes":attributes})).await; + assert_eq!(status, StatusCode::CREATED, "{started}"); + let task = poll(&app, "doubles-worker", "workflow").await; + let (status, body) = finish_task(&app, &task, json!([{"type":"schedule_activity","activity_type":"measure", + "arguments":envelope(Payload::Array(vec![])),"start_to_close_timeout":300}])).await; + assert_eq!(status, StatusCode::OK, "{body}"); + let activity = poll(&app, "doubles-worker", "activity").await; + let (status, body) = request(&app, "POST", &format!("/api/worker/activity-tasks/{}/heartbeat", activity["task_id"].as_str().unwrap()), + json!({"activity_attempt_id":activity["activity_attempt_id"],"lease_owner":activity["lease_owner"],"details":memo})).await; + assert_eq!(status, StatusCode::OK, "{body}"); + + // Read everything back through a fresh runtime, from storage alone. + runtime.close().await; + drop(app); + let runtime = database.open().await.unwrap(); + let app = router(runtime.clone()); + let run = format!("/api/workflows/doubles/runs/{}", started["run_id"].as_str().unwrap()); + let described = request(&app, "GET", &run, Value::Null).await.1; + let history = run_history(&app, &json!("doubles"), &started["run_id"]).await; + let workflow_started = history.as_array().unwrap().iter().find(|event| event["event_type"] == "WorkflowStarted").unwrap()["payload"].clone(); + let listed = request(&app, "GET", &format!("/api/workflows?query={}", "WorkflowId%20%3D%20%22doubles%22"), Value::Null).await.1; + let activities = request(&app, "GET", &format!("{run}/activities"), Value::Null).await.1; + let heartbeat = &activities["activities"][0]["last_heartbeat_progress"]["details"]; + for (index, text) in DOUBLES.iter().enumerate() { + let (key, name) = (format!("d{index}"), format!("Dbl{index}")); + exact(&described["memo"]["doubles"], &key, text, "described memo"); + exact(&workflow_started["memo"]["doubles"], &key, text, "WorkflowStarted memo"); + exact(&described["search_attributes"], &name, text, "described search attribute"); + exact(&listed["workflows"][0]["search_attributes"], &name, text, "listed search attribute"); + exact(&workflow_started["search_attributes"], &name, text, "WorkflowStarted search attribute"); + exact(heartbeat, &key, text, "heartbeat details"); + } + runtime.close().await; + database.remove().await; +} diff --git a/rust/tests/execution/memos.rs b/rust/tests/execution/memos.rs index ff8cf4630..8eff264dd 100644 --- a/rust/tests/execution/memos.rs +++ b/rust/tests/execution/memos.rs @@ -533,18 +533,6 @@ async fn raw_text(app: &Router, method: &str, path: &str, body: &str) -> (Status (status, String::from_utf8(bytes.to_vec()).unwrap()) } -/// The double a backend's JSON column holds for `text`. MySQL's json type -/// parses doubles itself and can land one ULP off, for PHP and native alike; -/// MariaDB stores JSON as text, and SQLite and PostgreSQL keep the text. -async fn json_column_double(database: &TestDatabase, text: &str) -> f64 { - if !matches!(database, TestDatabase::MySql { .. }) { - return text.parse().unwrap(); - } - let query = format!(r#"SELECT 'value', CAST(JSON_EXTRACT('{{"x":{text}}}', '$.x') AS CHAR)"#); - let rows = text_rows(database, "", "", &query).await; - rows[0].1.parse().unwrap() -} - // PHP's json_decode and json_encode give a double back with the digits it // came in with. Without serde_json's float_roundtrip, parsing can land one // ULP off, so a memo's 1.4820142857142857 was answered as …855. @@ -575,10 +563,8 @@ async fn memo_doubles_keep_every_digit_through_storage() { let exact: f64 = text.parse().unwrap(); let printed = serde_json::to_string(&exact).unwrap(); assert!(described.contains(&format!("\"d{index}\":{printed}")), "describe d{index} {text}: {described}"); - // History payloads live in a JSON column, so they hold the double - // that column stores, which the database itself names. - let stored = serde_json::to_string(&json_column_double(&database, text).await).unwrap(); - assert!(history.contains(&format!("\"d{index}\":{stored}")), "history d{index} {text} as {stored}: {history}"); + // History keeps every digit too, MySQL included (server#1016). + assert!(history.contains(&format!("\"d{index}\":{printed}")), "history d{index} {text}: {history}"); } runtime.close().await; database.remove().await; diff --git a/rust/tests/execution/query_contract_arguments.rs b/rust/tests/execution/query_contract_arguments.rs index b0353037a..3abcbb2e5 100644 --- a/rust/tests/execution/query_contract_arguments.rs +++ b/rust/tests/execution/query_contract_arguments.rs @@ -122,22 +122,15 @@ async fn a_registered_json_default_takes_phps_array_shape() { start(&app, "contract-query").await; let list = Payload::Array(vec![Payload::String("x".into()), Payload::String("y".into())]); assert_eq!(routed(&app, "listdef", envelope(Payload::Array(vec![]))).await, envelope(Payload::Array(vec![list.clone()]))); - // A map default reaches the worker in the key order the database keeps: - // the registered order, except that a MySQL json column orders keys by - // length, then bytes, for PHP as for native. MariaDB's json is text. - let version = text_rows(&database, "SELECT 'version', sqlite_version()", "SELECT 'version'::text, version()", - "SELECT 'version', VERSION()").await; - let mysql = matches!(database, TestDatabase::MySql { .. }) && !version[0].1.contains("MariaDB"); + // A map default reaches the worker in its registered key order on every + // backend. MySQL's json column used to order keys by length, then bytes; + // native schema 13 stores JSON as text there too (server#1016). let (z, a) = (("z".to_owned(), OrderedValue::Long(20)), ("a".to_owned(), OrderedValue::Long(10))); - let ordered = OrderedValue::Array(vec![OrderedValue::Map(if mysql { vec![a, z] } else { vec![z, a] })]); + let ordered = OrderedValue::Array(vec![OrderedValue::Map(vec![z, a])]); assert_eq!(routed(&app, "orderdef", envelope(Payload::Array(vec![]))).await, json!({"codec": "avro", "blob": ValueCodec::new().unwrap().encode_ordered_value(&ordered).unwrap()})); // Keys 0 to n-1 out of order are not a list, so PHP's encoder fails. - // MySQL stores them in order, which makes them a list. - if mysql { - assert_eq!(routed(&app, "descdef", envelope(Payload::Array(vec![]))).await, envelope(Payload::Array(vec![list.clone()]))); - } - for query in if mysql { vec!["gapdef"] } else { vec!["gapdef", "descdef"] } { + for query in ["gapdef", "descdef"] { let (status, body) = request(&app, "POST", &format!("/api/workflows/contract-query/query/{query}"), json!({"input": envelope(Payload::Array(vec![]))})).await; assert_eq!((status, body["reason"].clone()), (StatusCode::INTERNAL_SERVER_ERROR, json!("query_default_unencodable")), diff --git a/scripts/conformance/server-parity/json-doubles-contract.mjs b/scripts/conformance/server-parity/json-doubles-contract.mjs index df03e660e..7e7c4e8b6 100644 --- a/scripts/conformance/server-parity/json-doubles-contract.mjs +++ b/scripts/conformance/server-parity/json-doubles-contract.mjs @@ -7,10 +7,11 @@ import assert from 'node:assert/strict'; // - published PHP renders a typed double search attribute through PHP's // 14-digit `precision` in the run description and the visibility list // (`php_search_attribute_precision`); native keeps every digit; -// - on MySQL, the json column type parses doubles itself, so history -// payloads (WorkflowStarted, heartbeat details) hold MySQL's double for -// both implementations (`mysql_json_doubles`, read from MySQL 8.0 with -// CAST(... AS JSON)). MariaDB, SQLite and PostgreSQL keep the text. +// - on MySQL, published PHP's json column type parses doubles itself, so +// its history payloads (WorkflowStarted, heartbeat details) hold MySQL's +// double (`mysql_json_doubles`, read from MySQL 8.0 with CAST(... AS +// JSON)). Native stores JSON as exact text from schema 13 (server#1016), +// as MariaDB, SQLite and PostgreSQL keep it. // Expectations come from the fixture's texts, never from a server recording. const HISTORY_READERS = ['started_memo', 'started_search_attributes', 'heartbeat_details']; const ATTRIBUTE_READERS = ['described_search_attributes', 'listed_search_attributes']; @@ -39,7 +40,8 @@ export function checkJsonDoubles(fixture, observation) { assert.ok(Number.isFinite(value) && !Number.isInteger(value), `fixture double ${name} has a fraction`); return value; }; - const stored = (name) => storage === 'mysql_json' && Object.hasOwn(definition.mysql_json_doubles, name) + const stored = (name) => storage === 'mysql_json' && implementation === null + && Object.hasOwn(definition.mysql_json_doubles, name) ? Number(definition.mysql_json_doubles[name]) : exact(name); const attribute = (name) => implementation === null ? Number(exact(name).toPrecision(definition.php_search_attribute_precision)) : exact(name); diff --git a/scripts/conformance/server-parity/mysql-schema.php b/scripts/conformance/server-parity/mysql-schema.php index caddef187..d30915538 100644 --- a/scripts/conformance/server-parity/mysql-schema.php +++ b/scripts/conformance/server-parity/mysql-schema.php @@ -108,7 +108,7 @@ function catalog(PDO $pdo): array } $pdo->exec('ROLLBACK'); $pdo = connection($argv[3]); -$native = array_values(array_filter(projectMysqlScopedDispatch(projectMysqlReadyOrigin(catalog($pdo))), static fn (array $row): bool => ! in_array($row[0], ['dw_server_schema', '_sqlx_migrations', 'dw_task_completions', 'dw_poll_receipts', 'dw_query_cache', 'dw_worker_registration_incarnations', 'dw_worker_session_holders', 'dw_stream_reclaims', 'dw_service_call_deadlines', 'dw_service_admission_counters', 'dw_external_payload_deletions', 'dw_run_command_contracts'], true))); +$native = array_values(array_filter(projectMysqlJsonText(projectMysqlScopedDispatch(projectMysqlReadyOrigin(catalog($pdo))), $php), static fn (array $row): bool => ! in_array($row[0], ['dw_server_schema', '_sqlx_migrations', 'dw_task_completions', 'dw_poll_receipts', 'dw_query_cache', 'dw_worker_registration_incarnations', 'dw_worker_session_holders', 'dw_stream_reclaims', 'dw_service_call_deadlines', 'dw_service_admission_counters', 'dw_external_payload_deletions', 'dw_run_command_contracts'], true))); $pdo->exec('ROLLBACK'); // Native polling uses an additional index on an existing table. Retain all // original column, index and constraint definitions in the comparison. diff --git a/scripts/conformance/server-parity/ready-origin-schema.php b/scripts/conformance/server-parity/ready-origin-schema.php index ab27bd0a1..24653b783 100644 --- a/scripts/conformance/server-parity/ready-origin-schema.php +++ b/scripts/conformance/server-parity/ready-origin-schema.php @@ -108,3 +108,35 @@ function projectPostgresScopedDispatch(array $rows): array return $result; } + +// Native MySQL schema 13 stores each json column as exact utf8mb4_bin text +// with an enforced json_valid check (server#1016). For every json column in +// published PHP's table, map exactly that native definition and its named +// check back to PHP's json definition. Any other difference still fails. +function projectMysqlJsonText(array $native, array $php): array +{ + $source = array_column($php, 1, 0); + foreach ($native as &$row) { + [$table, $sql] = $row; + if (! isset($source[$table])) { + continue; + } + preg_match_all("/^ `(\\w+)` json( NOT NULL| DEFAULT NULL)( COMMENT '[^']*')?,$/m", $source[$table], $columns, PREG_SET_ORDER); + foreach ($columns as $column) { + [$line, $name, $null] = $column; + $comment = $column[3] ?? ''; + $pattern = '/^ `'.preg_quote($name, '/').'` longtext( CHARACTER SET utf8mb4)? COLLATE utf8mb4_bin' + .($null === ' NOT NULL' ? ' NOT NULL' : '').preg_quote($comment, '/').',$/m'; + $sql = preg_replace($pattern, $line, $sql, -1, $replaced); + $check = ",\n CONSTRAINT `dw_json_text_".substr(hash('sha256', $table."\0".$name), 0, 20)."` CHECK (json_valid(`{$name}`))"; + if ($replaced !== 1 || substr_count($sql, $check) !== 1) { + throw new RuntimeException("Native JSON text definition mismatch: {$table}.{$name}"); + } + $sql = str_replace($check, '', $sql); + } + $row[1] = $sql; + } + unset($row); + + return $native; +} diff --git a/tests/Fixtures/ServerParity/json-doubles.json b/tests/Fixtures/ServerParity/json-doubles.json index f75ab7a29..9c78c7562 100644 --- a/tests/Fixtures/ServerParity/json-doubles.json +++ b/tests/Fixtures/ServerParity/json-doubles.json @@ -2,7 +2,7 @@ "schema": "durable-workflow.server-parity-fixture/v1", "id": "json-doubles", "review_state": "reviewed", - "contract": "JSON doubles keep every digit (server#1010). Published Server 2.11.8 reads request bodies with json_decode and answers with json_encode, so a double comes back as the same double it was sent as. Over raw HTTP in a fresh namespace, one run starts with the fixture's doubles as a memo and as registered double search attributes, and its activity heartbeats with the same doubles as details. Each value read back from the run description (memo and search attributes), the visibility list (search attributes), the WorkflowStarted event (memo and search attributes) and the run's activity metadata (last_heartbeat_progress.details) must be exactly the double its fixture text names: one ULP off is a failure. Two reviewed exceptions apply. Published PHP renders a typed double search attribute through PHP's 14-digit precision in the run description and the visibility list (php_search_attribute_precision), so 1.4820142857142857 reads back as 1.4820142857143; native keeps every digit there. On MySQL, the json column type parses doubles itself, so the history payloads (WorkflowStarted and the heartbeat's details) hold MySQL's double for both implementations: mysql_json_doubles names the values MySQL 8.0's CAST(... AS JSON) stores, and every other double is unchanged. MariaDB stores JSON as text and, like SQLite and PostgreSQL, keeps every digit. The probe records the implementation from cluster info and the storage from the job's database image; any other implementation or storage is refused. The expectations come from the input texts and these two reviewed facts, never from a recording. Embedded applications have no HTTP request bodies, so the case is explicitly inapplicable there.", + "contract": "JSON doubles keep every digit (server#1010). Published Server 2.11.8 reads request bodies with json_decode and answers with json_encode, so a double comes back as the same double it was sent as. Over raw HTTP in a fresh namespace, one run starts with the fixture's doubles as a memo and as registered double search attributes, and its activity heartbeats with the same doubles as details. Each value read back from the run description (memo and search attributes), the visibility list (search attributes), the WorkflowStarted event (memo and search attributes) and the run's activity metadata (last_heartbeat_progress.details) must be exactly the double its fixture text names: one ULP off is a failure. Two reviewed exceptions apply. Published PHP renders a typed double search attribute through PHP's 14-digit precision in the run description and the visibility list (php_search_attribute_precision), so 1.4820142857142857 reads back as 1.4820142857143; native keeps every digit there. On MySQL, published PHP's json column type parses doubles itself, so its history payloads (WorkflowStarted and the heartbeat's details) hold MySQL's double: mysql_json_doubles names the values MySQL 8.0's CAST(... AS JSON) stores, and every other double is unchanged. Native stores JSON as exact text on MySQL from schema 13 (server#1016). MariaDB stores JSON as text and, like SQLite and PostgreSQL, keeps every digit. The probe records the implementation from cluster info and the storage from the job's database image; any other implementation or storage is refused. The expectations come from the input texts and these two reviewed facts, never from a recording. Embedded applications have no HTTP request bodies, so the case is explicitly inapplicable there.", "workflow_type": "parity.v1.echo", "input": { "message": "json doubles λ", diff --git a/tests/Unit/NativeMysqlJsonTextSchemaTest.php b/tests/Unit/NativeMysqlJsonTextSchemaTest.php new file mode 100644 index 000000000..1ae82e393 --- /dev/null +++ b/tests/Unit/NativeMysqlJsonTextSchemaTest.php @@ -0,0 +1,41 @@ +assertSame($old, projectMysqlJsonText($new, $old)); + // Live SHOW CREATE names the character set where the table default + // collation differs from the column's. + $raw = array_map(static fn (array $row): array => str_ends_with($row[1], 'COLLATE=utf8mb4_bin') ? $row : [$row[0], + str_replace('longtext COLLATE utf8mb4_bin', 'longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin', $row[1])], $new); + $this->assertSame($old, projectMysqlJsonText($raw, $old)); + // MariaDB's PHP layout has no json column: nothing changes. + $maria = json_decode(file_get_contents(__DIR__.'/../../rust/migrations/mariadb/0012_catalog.json'), true, flags: JSON_THROW_ON_ERROR); + $this->assertSame($maria, projectMysqlJsonText($maria, $maria)); + foreach ([ + 'collation' => ['`retry_policy` longtext COLLATE utf8mb4_bin,', '`retry_policy` longtext COLLATE utf8mb4_unicode_ci,'], + 'missing check' => [",\n CONSTRAINT `dw_json_text_da40cddf645f92a0f5d3` CHECK (json_valid(`retry_policy`))", ''], + 'renamed check' => ['dw_json_text_da40cddf645f92a0f5d3', 'dw_json_text_000000000000000000000'], + ] as $label => [$from, $to]) { + $changed = array_map(static fn (array $row): array => $row[0] === 'activity_executions' + ? [$row[0], str_replace($from, $to, $row[1])] : $row, $new); + try { + projectMysqlJsonText($changed, $old); + $this->fail("Changed native JSON text definition was hidden: {$label}."); + } catch (RuntimeException $error) { + $this->assertSame('Native JSON text definition mismatch: activity_executions.retry_policy', $error->getMessage()); + } + } + } +} diff --git a/tests/Unit/ServerParityJsonDoublesContractTest.mjs b/tests/Unit/ServerParityJsonDoublesContractTest.mjs index 8b5c5e986..687cec2ca 100644 --- a/tests/Unit/ServerParityJsonDoublesContractTest.mjs +++ b/tests/Unit/ServerParityJsonDoublesContractTest.mjs @@ -19,7 +19,8 @@ const projection = {scope: 'json_doubles', values: names.length}; // JSON text and read back, as the recorder's observation file is. const observation = (implementation, storage) => { const attributes = implementation === null ? php14 : exact; - const history = storage === 'mysql_json' ? mysql : exact; + // Native keeps exact text on MySQL from schema 13 (server#1016). + const history = storage === 'mysql_json' && implementation === null ? mysql : exact; return JSON.parse(JSON.stringify({mode: 'http', json_doubles: {applicable: true, implementation, storage, described_memo: exact, described_search_attributes: keyed(attributes), listed_search_attributes: keyed(attributes), started_memo: history, started_search_attributes: keyed(history), heartbeat_details: history}})); @@ -78,7 +79,8 @@ for (const [label, implementation, storage, mutate] of [ (state) => { state.described_search_attributes = keyed(php14); }], ['PHP with exact search attributes', null, 'exact', (state) => { state.listed_search_attributes = keyed(exact); }], ['MySQL rounding on exact storage', 'rust', 'exact', (state) => { state.heartbeat_details = mysql; }], - ['exact history on MySQL storage', 'rust', 'mysql_json', (state) => { state.started_memo = exact; }], + ['exact history from PHP on MySQL storage', null, 'mysql_json', (state) => { state.started_memo = exact; }], + ['native MySQL rounding on MySQL storage', 'rust', 'mysql_json', (state) => { state.heartbeat_details = mysql; }], ['MySQL rounding in the described memo', null, 'mysql_json', (state) => { state.described_memo = mysql; }], ['an unknown implementation', 'go', 'exact', () => {}], ['an unnamed storage', 'rust', 'exact', (state) => { delete state.storage; }],